├── .EditorConfig ├── .gitignore ├── AccessList.yaml ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pom.xml ├── settings.gradle └── src ├── main └── java │ ├── Basics.kt │ ├── MessageService.kt │ ├── Test.kt │ ├── basics │ ├── API.kt │ ├── Color.kt │ ├── Factorial.kt │ ├── Person.kt │ ├── Pizza.kt │ ├── Tree.kt │ ├── TreeOperations.kt │ └── Weather.java │ ├── collections │ ├── BestStudents.kt │ ├── Collections.kt │ ├── PassingStudents.kt │ ├── PlusAt.kt │ ├── PrimeAccessList.kt │ ├── QuickSort.kt │ ├── Student.kt │ └── YamlReader.kt │ ├── corotuines │ ├── Downloader.kt │ ├── Fibonacci.kt │ ├── FlowKata.kt │ ├── Main.kt │ ├── Sieve.kt │ ├── Structured.kt │ ├── backend │ │ ├── Api.kt │ │ ├── ApiDsl.kt │ │ ├── Database.kt │ │ └── EmailService.kt │ ├── continuation │ │ └── ContinuationSteal.kt │ ├── examples │ │ ├── 1.kt │ │ ├── 10.kt │ │ ├── 11.kt │ │ ├── 12.kt │ │ ├── 13.kt │ │ ├── 14.kt │ │ ├── 15.kt │ │ ├── 2.kt │ │ ├── 3.kt │ │ ├── 4.kt │ │ ├── 5.kt │ │ ├── 6.kt │ │ ├── 7.kt │ │ ├── 8.kt │ │ ├── 9.kt │ │ ├── Delay.kt │ │ ├── Massive.kt │ │ ├── Numbers.kt │ │ ├── s1.kt │ │ ├── s2.kt │ │ ├── s3.kt │ │ └── s4.kt │ ├── request │ │ └── Request.kt │ └── ui │ │ ├── BasePresenter.kt │ │ └── MainPresenter.kt │ ├── delegates │ └── MutableLazy.kt │ ├── dsl │ ├── Announcements.kt │ ├── TableDSL.kt │ └── UsersTable.kt │ ├── extra │ ├── Permutations.kt │ └── Powerset.kt │ ├── functional │ ├── Callbacks.kt │ ├── Functional.kt │ ├── Product.kt │ ├── Rational.kt │ ├── References.kt │ ├── Repeat.kt │ └── UserDocument.kt │ ├── generics │ ├── Consumer.kt │ ├── Generics.kt │ └── Response.kt │ ├── javatask │ ├── JavaClass.java │ ├── JavaPerson.java │ ├── KotlinClass.kt │ ├── KotlinPerson.kt │ └── TopLevel.kt │ ├── nullability │ ├── MessageUtil.java │ └── Task.kt │ ├── operators │ └── Money.kt │ └── types │ ├── Relations.kt │ └── Types.kt └── test └── java ├── BasicsTest.kt ├── SendMessageTest.kt ├── Test.kt ├── Utils.kt ├── basics ├── APITest.kt ├── FactorialTest.kt ├── PersonTest.kt └── TreeOperationsTest.kt ├── collections ├── BestStudentsListTest.kt ├── MapTest.kt ├── PassingStudentsListTest.kt ├── PlusAtTest.kt ├── QuickSortTest.kt └── StudentsData.kt ├── corotuines ├── CoroutineTest.kt ├── FibonacciTest.kt ├── FlowKataTests.kt ├── Main.kt ├── SieveTest.kt ├── continuation │ └── ContinuationStealTest.kt ├── request │ └── RequestTest.kt └── ui │ ├── BasePresenterTest.kt │ └── CoroutineExceptionHandlingTest.kt ├── delegates ├── LateinitTest.kt └── MutableLazyTest.kt ├── dsl ├── AnnouncementsListTest.kt ├── HtmlDslTest.kt └── UsersTableTest.kt ├── extra ├── PermutationTest.kt └── PowersetTest.kt ├── functional ├── CallbacksTest.kt ├── FunctionalTest.kt ├── ObservableValueTest.kt ├── ProductTest.kt ├── RationalTest.kt └── RepeatTest.kt └── nullability ├── Task.kt └── TaskThrowing.kt /.EditorConfig: -------------------------------------------------------------------------------- 1 | [*.{kt,kts}] 2 | indent_size=4 3 | insert_final_newline=true 4 | max_line_length=off -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | build 3 | out 4 | .gradle 5 | lib -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Kotlin workshop 3 | 4 | This project containes exercises and examples for [Kt. Academy Kotlin workshop](https://kt.academy/Kotlin_in_Backend). 5 | 6 | Workshop coveres: 7 | * Basic Kotlin structures 8 | * Functions on different levels and functional style 9 | * Kotlin OO programming 10 | * Data classes 11 | * Sealed classes 12 | * Object expression, object declaration and companion object 13 | * Kotlin type system 14 | * Extension functions 15 | * Functional programming in Kotlin 16 | * Collections and string processing 17 | * Scope functions (let, apply, run, also, with, takeIf, takeUnless) 18 | * Generic classes and functions, making own collection processing function 19 | * Kotlin generic type parameter declarations and modifiers 20 | * Kotlin property and interface delegates 21 | * Reflection in Kotlin, and how to use Java reflection in Kotlin 22 | * DSL usage and creation 23 | * Interoperability between Kotlin and Java 24 | * Basics of Kotlin coroutines 25 | * Rules of idiomatic Kotlin 26 | * Kotlin style guides 27 | 28 | If you are interested in having this workshop in your company, you can find details [here](https://kt.academy/Kotlin_in_Backend) or just fill [the form](https://marcinmoskala.typeform.com/to/StT0w1). 29 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.9.0' 3 | ext.kotlin_coroutines_version = '1.7.2' 4 | repositories { 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 9 | } 10 | } 11 | 12 | apply plugin: 'kotlin' 13 | 14 | repositories { 15 | mavenCentral() 16 | } 17 | 18 | dependencies { 19 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 20 | implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" 21 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" 22 | 23 | // Callback exercise 24 | implementation "com.squareup.retrofit2:retrofit:2.4.0" 25 | implementation "com.squareup.retrofit2:converter-jackson:2.4.0" 26 | implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.7" 27 | 28 | // collections access list 29 | implementation('com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.0') 30 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.+") 31 | 32 | testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version" 33 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.0.0' 34 | testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" 35 | } 36 | 37 | compileKotlin { 38 | kotlinOptions { 39 | jvmTarget = '1.8' //Good to target Java 8 unless you need 7 40 | javaParameters = true //Will retain parameter names for Java reflection 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcinMoskala/advanced-kotlin-workshop-tasks/3e630849978ca82176097d21347a54c35ae38097/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.marcinmoskala 7 | Workshop 8 | 1.0 9 | 10 | 11 | 1.9.0 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | 19 | org.jetbrains.kotlin 20 | kotlin-maven-plugin 21 | ${kotlin.version} 22 | 23 | 24 | compile 25 | 26 | compile 27 | 28 | 29 | 30 | ${project.basedir}/src/main/kotlin 31 | ${project.basedir}/src/main/java 32 | 33 | 34 | 35 | 36 | test-compile 37 | 38 | test-compile 39 | 40 | 41 | 42 | ${project.basedir}/src/test/kotlin 43 | ${project.basedir}/src/test/java 44 | 45 | 46 | 47 | 48 | 49 | 50 | -java-parameters 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-compiler-plugin 57 | 3.5.1 58 | 59 | 1.8 60 | 1.8 61 | 62 | 63 | 64 | 65 | default-compile 66 | none 67 | 68 | 69 | 70 | default-testCompile 71 | none 72 | 73 | 74 | java-compile 75 | compile 76 | 77 | compile 78 | 79 | 80 | 81 | java-test-compile 82 | test-compile 83 | 84 | testCompile 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | central 95 | https://jcenter.bintray.com 96 | 97 | 98 | 99 | 100 | 101 | org.jetbrains.kotlin 102 | kotlin-stdlib 103 | ${kotlin.version} 104 | 105 | 106 | org.jetbrains.kotlin 107 | kotlin-reflect 108 | ${kotlin.version} 109 | 110 | 111 | org.jetbrains.kotlinx 112 | kotlinx-coroutines-core 113 | 1.7.2 114 | 115 | 116 | org.jetbrains.kotlinx 117 | kotlinx-coroutines-test 118 | 1.7.2 119 | test 120 | 121 | 122 | com.squareup.retrofit2 123 | retrofit 124 | 2.4.0 125 | 126 | 127 | com.squareup.retrofit2 128 | converter-jackson 129 | 2.4.0 130 | 131 | 132 | com.fasterxml.jackson.dataformat 133 | jackson-dataformat-yaml 134 | 2.7.4 135 | 136 | 137 | com.fasterxml.jackson.module 138 | jackson-module-kotlin 139 | 2.9.7 140 | 141 | 142 | org.jetbrains.kotlin 143 | kotlin-test-junit 144 | ${kotlin.version} 145 | test 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Workshop' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/Basics.kt: -------------------------------------------------------------------------------- 1 | // https://en.wikipedia.org/wiki/Greatest_common_divisor 2 | fun gcd(a: Int, b: Int): Int = TODO() 3 | 4 | // fizzBuzz function that returns String that represents what should be said in the FizzBuzz game for each number between 1 and 100. 5 | // We list all this numbers in new lines, but we replace some of them with: 6 | // “Fizz” if number is divisible by 3 7 | // “Buzz” if number is divisible by 5 8 | // “FizzBuzz” if number is divisible both by 3 and 5 (by 15) 9 | // Print elements using `console.println` 10 | fun fizzBuzz(console: Console = PrintingConsole) { 11 | TODO() 12 | } 13 | 14 | interface Console { 15 | fun println(text: String) 16 | } 17 | 18 | object PrintingConsole : Console { 19 | override fun println(text: String) { 20 | kotlin.io.println(text) 21 | } 22 | } 23 | 24 | // Fibonacci number that starts from 1 and 1 (fib(0) == 1, fib(1) == 1, fib(2) == 2, fib(3) == 3, fib(4) == 5, fib(5) == 8) 25 | // https://en.wikipedia.org/wiki/Fibonacci_number 26 | fun fib(n: Int): Int = TODO() -------------------------------------------------------------------------------- /src/main/java/MessageService.kt: -------------------------------------------------------------------------------- 1 | package sendmessage 2 | 3 | class MessageService( 4 | val messageSender: MessageSender 5 | ) { 6 | // TODO: Implement `sendMessage` function here 7 | // With parameters: 8 | // * `to` of type `String` and default value "all" 9 | // * `title` of type `String` and default value "" 10 | // * `content` of type `String` and default value "" 11 | // Without result type (or returning `Unit`) 12 | // Function should get emails using `findEmailAddresses` 13 | // Then for each email, it should send emails using `messageSender` function `sendEmail` 14 | // For iterate over emails, use 15 | // for(email in emails) { 16 | // ... 17 | // } 18 | 19 | private fun findEmailAddresses(to: String): List = 20 | if(to == "all") allEmailAddresses 21 | else allEmailAddresses.filter { it.address == to } 22 | 23 | private val allEmailAddresses = listOf( 24 | EmailAddress("alex@gmail.com"), 25 | EmailAddress("jake@gmail.com"), 26 | EmailAddress("leon@gmail.com"), 27 | EmailAddress("ally@gmail.com"), 28 | ) 29 | } 30 | 31 | data class EmailAddress(val address: String) 32 | 33 | interface MessageSender { 34 | fun sendEmail(email: EmailAddress, title: String, content: String) 35 | } 36 | 37 | data class Email( 38 | val email: EmailAddress, 39 | val title: String, 40 | val content: String 41 | ) 42 | -------------------------------------------------------------------------------- /src/main/java/Test.kt: -------------------------------------------------------------------------------- 1 | fun main() { 2 | print("Hello, World") 3 | } -------------------------------------------------------------------------------- /src/main/java/basics/API.kt: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | import generics.Response 4 | 5 | class StudentController( 6 | private val studentRepository: StudentRepository, 7 | private val analyticsRepository: AnalyticsRepository 8 | ) { 9 | 10 | @GetMapping("/student/{id}") 11 | fun getStudent(@PathVariable id: Long): StudentAPI { 12 | TODO() 13 | } 14 | 15 | @GetMapping("/student") 16 | fun getStudents(): List { 17 | TODO() 18 | } 19 | } 20 | 21 | data class StudentAPI( 22 | val name: String, 23 | val surname: String 24 | ) 25 | 26 | @Entity 27 | data class StudentEntity( 28 | @Id @GeneratedValue 29 | val id: Long = -1, 30 | val firstName: String, 31 | val lastName: String 32 | ) 33 | 34 | interface StudentRepository { 35 | 36 | fun findStudent(id: Long): StudentEntity? 37 | fun findStudentResult(id: Long): Response 38 | fun getAllStudents(): List 39 | } 40 | 41 | object NotFoundException : Throwable() 42 | 43 | interface AnalyticsRepository { 44 | 45 | fun getStudentByIdCount(id: Long): Int 46 | fun setStudentByIdCount(id: Long, count: Int) 47 | } 48 | 49 | data class ApiError(val code: Int, override val message: String) : Throwable(message) 50 | 51 | annotation class Entity 52 | annotation class Id 53 | annotation class GeneratedValue 54 | annotation class GetMapping(val path: String) 55 | annotation class PathVariable 56 | -------------------------------------------------------------------------------- /src/main/java/basics/Color.kt: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | enum class Color { 4 | BLUE, RED, YELLOW 5 | } -------------------------------------------------------------------------------- /src/main/java/basics/Factorial.kt: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | // factorial(5) = 5! = 5 * 4 * 3 * 2 * 1 = 120 4 | // https://en.wikipedia.org/wiki/Factorial 5 | fun factorial(n: Int): Long = TODO() -------------------------------------------------------------------------------- /src/main/java/basics/Person.kt: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | interface Person { 4 | val name: String 5 | val age: Int 6 | val canBuyAlcohol: Boolean 7 | 8 | fun helloText(): String 9 | 10 | fun cheerText(person: Person): String 11 | } 12 | 13 | fun main(args: Array) { 14 | val businessman: Person = TODO("Use Businessman constructor here once it is implemented") 15 | val student: Person = TODO("Use Student constructor here once it is implemented") 16 | 17 | println(businessman.helloText()) 18 | println(student.helloText()) 19 | 20 | println(businessman.cheerText(student)) 21 | println(student.cheerText(businessman)) 22 | 23 | fun sayIfCanBuyAlcohol(person: Person) { 24 | val modal = if (person.canBuyAlcohol) "can" else "can't" 25 | println("${person.name} $modal buy alcohol") 26 | } 27 | 28 | sayIfCanBuyAlcohol(businessman) 29 | sayIfCanBuyAlcohol(student) 30 | } -------------------------------------------------------------------------------- /src/main/java/basics/Pizza.kt: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | data class Pizza( 4 | val cheese: Int = 0, 5 | val pineapple: Int = 0, 6 | val ham: Int = 0, 7 | val egg: Int = 0, 8 | val spinach: Int = 0 9 | ) 10 | 11 | fun main() { 12 | // val pizza1 = Pizza.hawaiian() 13 | // val pizza2 = PizzaFactory.hawaiian() 14 | // assert(pizza1 == Pizza(cheese = 1, pineapple = 1, ham = 1)) 15 | // assert(pizza2 == Pizza(cheese = 1, pineapple = 1, ham = 1)) 16 | } -------------------------------------------------------------------------------- /src/main/java/basics/Tree.kt: -------------------------------------------------------------------------------- 1 | package basics.tree 2 | 3 | // TODO: Clean it up 4 | abstract class Tree { 5 | override fun toString(): String { 6 | return treeToString(this, StringBuilder()).toString() 7 | } 8 | } 9 | 10 | class Leaf(val value: String) : Tree() 11 | class Node(val left: Tree, val right: Tree) : Tree() 12 | 13 | private fun treeToString(tree: Tree, sb: StringBuilder): StringBuilder { 14 | if (tree is Leaf) { 15 | val leaf = tree as Leaf 16 | sb.append(leaf.value) 17 | } else if (tree is Node) { 18 | val node = tree as Node 19 | treeToString(node.left, sb) 20 | sb.append(", ") 21 | treeToString(node.right, sb) 22 | } 23 | return sb 24 | } -------------------------------------------------------------------------------- /src/main/java/basics/TreeOperations.kt: -------------------------------------------------------------------------------- 1 | package basics.treeoperations 2 | 3 | sealed class Tree { 4 | override fun toString(): String = when (this) { 5 | is Leaf -> value.toString() 6 | is Node -> "($left, $right)" 7 | } 8 | } 9 | 10 | data class Leaf(val value: T) : Tree() 11 | data class Node(val left: Tree, val right: Tree) : Tree() -------------------------------------------------------------------------------- /src/main/java/basics/Weather.java: -------------------------------------------------------------------------------- 1 | package basics; 2 | 3 | public class Weather { 4 | 5 | public void updateWeather(int degrees) { 6 | String description; 7 | Color color; 8 | if (degrees < 5) { 9 | description = "cold"; 10 | color = Color.BLUE; 11 | } else if (degrees < 23) { 12 | description = "mild"; 13 | color = Color.YELLOW; 14 | } else { 15 | description = "hot"; 16 | color = Color.RED; 17 | } 18 | // ... 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/java/collections/BestStudents.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // Implement makeBestStudentsList method to display the best 10 students, 4 | // so they can get an internship. You should compare them by their result 5 | // (higher is better). To get an internship, students need to have got 6 | // at least 30 points in the semester and a result of at least 80. 7 | // The best student gets $5000, the next 3 get $3000 and the next 6 get $1000. 8 | // Display students in alphabetical order (compare first surname then name) 9 | // in the format “{name} {surname}, ${internship size}” 10 | fun List.makeBestStudentsList(): String = TODO() -------------------------------------------------------------------------------- /src/main/java/collections/Collections.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // To prevent unintentional stdlib functions usage 4 | import kotlin.collections.flatMap as stdlibFlatMap 5 | import kotlin.collections.map as stdlibMap 6 | import kotlin.collections.filter as stdlibFilter 7 | import kotlin.collections.onEach as stdlibOnEach 8 | 9 | inline fun > C.onEach(operation: (T) -> Unit): C { 10 | for (elem in this) { 11 | operation(elem) 12 | } 13 | return this 14 | } 15 | 16 | inline fun Iterable.flatMap(transformation: (T) -> Iterable): List { 17 | val list = ArrayList() 18 | for (elem in this) { 19 | list.addAll(transformation(elem)) 20 | } 21 | return list 22 | } 23 | 24 | inline fun Iterable.filter(predicate: (T) -> Boolean): List { 25 | val list = ArrayList() 26 | for (elem in this) { 27 | if (predicate(elem)) { 28 | list.add(elem) 29 | } 30 | } 31 | return list 32 | } 33 | 34 | fun main(args: Array) { 35 | val numbers = 1..10 36 | val names = listOf("Mike", "Jane", "Marcin", "John", "James") 37 | 38 | numbers.onEach { print(it) } // 12345678910 39 | println() 40 | names.onEach { print(it) } // MikeJaneMarcinJohnJames 41 | println() 42 | 43 | println(names.filter { it.startsWith("J") }) // [Jane, John, James] 44 | println(names.filter { it.startsWith("M") }) // [Mike, Marcin] 45 | 46 | println(names.flatMap { it.toList() }) // [M, i, k, e, J, a, n, e, M, a, r, c, i, n, J, o, h, n, J, a, m, e, s] 47 | println(numbers.flatMap { listOf(it, it + 10) }) // [1, 11, 2, 12, 3, 13, 4, 14, 5, 15, 6, 16, 7, 17, 8, 18, 9, 19, 10, 20] 48 | 49 | // println(names.map { it.toUpperCase() }) // [MIKE, JANE, MARCIN, JOHN, JAMES] 50 | // println(numbers.map { it * 10 }) // [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/collections/PassingStudents.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // Implement makePassingStudentsListText method to display a list of students 4 | // who have got more than 15 points in the semester and a result of at least 50. 5 | // Display in alphabetical order (surname then name), in the format: 6 | // “{name} {surname}, {result}” 7 | fun List.makePassingStudentsListText(): String = TODO() -------------------------------------------------------------------------------- /src/main/java/collections/PlusAt.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | fun List.plusAt(index: Int, element: T): List { 4 | TODO() 5 | } -------------------------------------------------------------------------------- /src/main/java/collections/PrimeAccessList.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import java.io.File 4 | import kotlin.system.measureTimeMillis 5 | 6 | val file = File("AccessList.yaml") 7 | val yamlReader = YamlReader() 8 | 9 | class PrimeAccessListRepository { 10 | private val accessList: PrimeAccessList = yamlReader.readYaml(file) 11 | 12 | fun isOnAllowList(userId: Long): Boolean = TODO() 13 | 14 | fun isOnDenyList(userId: Long): Boolean = TODO() 15 | } 16 | 17 | class PrimeAccessList( 18 | val entries: List 19 | ) 20 | 21 | class PrimeAccessEntry( 22 | val userId: Long, 23 | val allowList: Boolean, 24 | val allowListReason: String?, 25 | val denyList: Boolean, 26 | val denyListReason: String? 27 | ) 28 | 29 | fun main() { 30 | val repo = PrimeAccessListRepository() 31 | 32 | measureTimeMillis { 33 | for (userId in 1L..10_000L) { 34 | repo.isOnAllowList(userId) 35 | } 36 | }.let { println("Checking 10_000 ids by isOnAllowList took $it") } 37 | 38 | measureTimeMillis { 39 | for (userId in 1L..10_000L) { 40 | repo.isOnDenyList(userId) 41 | } 42 | }.let { println("Checking 10_000 ids by isOnDenyList took $it") } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/collections/QuickSort.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // TODO: Quick sort should take first element (pivot), then split rest to bigger then pivot and smaller and finally return 4 | // first smaller sorted, then pivot and finally bigger sorted 5 | fun > List.quickSort(): List { 6 | TODO() 7 | } -------------------------------------------------------------------------------- /src/main/java/collections/Student.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | data class Student( 4 | val name: String, 5 | val surname: String, 6 | val result: Double, 7 | val pointsInSemester: Int 8 | ) -------------------------------------------------------------------------------- /src/main/java/collections/YamlReader.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import com.fasterxml.jackson.core.JsonParser 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import com.fasterxml.jackson.databind.SerializationFeature 6 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory 7 | import com.fasterxml.jackson.module.kotlin.KotlinModule 8 | import java.io.File 9 | 10 | class YamlReader { 11 | private val mapper = ObjectMapper(YAMLFactory()).apply { 12 | registerModule(KotlinModule()) 13 | configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) 14 | disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 15 | } 16 | 17 | fun readYaml(file: File, clazz: Class): T = 18 | mapper.readValue(file, clazz) 19 | 20 | inline fun readYaml(file: File): T = 21 | readYaml(file, T::class.java) 22 | 23 | fun writeYaml(file: File, value: T) = 24 | mapper.writeValue(file, value) 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/corotuines/Downloader.kt: -------------------------------------------------------------------------------- 1 | package corotuines 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.random.Random 5 | 6 | class User(val name: String) 7 | 8 | class NetworkService { 9 | suspend fun getUser(): User { 10 | delay(2) 11 | return User(Random.nextLong().toString()) 12 | } 13 | } 14 | 15 | class UserDownloader(val api: NetworkService) { 16 | private val users = mutableListOf() 17 | 18 | fun all(): List = users 19 | 20 | suspend fun downloadNext(num: Int) = coroutineScope { 21 | repeat(num) { 22 | val newUser = api.getUser() 23 | users.add(newUser) 24 | } 25 | } 26 | } 27 | 28 | fun main() = runBlocking(Dispatchers.Default) { 29 | val downloader = UserDownloader(NetworkService()) 30 | coroutineScope { 31 | repeat(1000) { 32 | launch { 33 | downloader.downloadNext(1000) 34 | } 35 | } 36 | } 37 | print(downloader.all().size) 38 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/Fibonacci.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | val fibonacci: Sequence = sequence { 4 | TODO() 5 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/FlowKata.kt: -------------------------------------------------------------------------------- 1 | package corotuines 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | 5 | // Produces a flow of Unit 6 | // For instance producingUnits(5) -> [Unit, Unit, Unit, Unit, Unit] 7 | fun producingUnits(num: Int): Flow = TODO() 8 | 9 | // Adds a delay of time `timeMillis` between elements 10 | fun Flow.delayEach(timeMillis: Long): Flow = TODO() 11 | 12 | // Should transform Unit's to toggled boolean value starting from true 13 | // For instance flowOf(Unit, Unit, Unit, Unit).toNextNumbers() -> [true, false, true, false] 14 | fun Flow<*>.toToggle(): Flow = TODO() 15 | 16 | // Should transform Unit's to next numbers startling from 1 17 | // For instance flowOf(Unit, Unit, Unit, Unit).toNextNumbers() -> [1, 2, 3, 4] 18 | fun Flow<*>.toNextNumbers(): Flow = TODO() 19 | 20 | // Produces not only elements, but the whole history till now 21 | // For instance flowOf(1, "A", 'C').withHistory() -> [[], [1], [1, A], [1, A, C]] 22 | fun Flow.withHistory(): Flow> = TODO() 23 | 24 | // Should create a flow that every `tickEveryMillis` should emit next numbers from `startNum` to `endNum` 25 | fun makeTimer(tickEveryMillis: Long, startNum: Int, endNum: Int): Flow = TODO() 26 | 27 | // Based on two light switches, should decide if the general light should be switched on. 28 | // Should be if one is true and another is false 29 | fun makeLightSwitch(switch1: Flow, switch2: Flow): Flow = TODO() 30 | 31 | // Based on two light switches, should decide if the general light should be switched on. 32 | // Should be if one is turned on and another is off 33 | // At the beginning, both switches are off, and each action toggles 34 | fun makeLightSwitchToggle(switch1: Flow, switch2: Flow): Flow = TODO() 35 | 36 | fun polonaisePairing(track1: Flow, track2: Flow): Flow> = TODO() 37 | 38 | data class Person(val name: String) 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/corotuines/Main.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.runBlocking 5 | 6 | fun main(): Unit = runBlocking { 7 | println("Started!") 8 | test() 9 | println("Done.") 10 | } 11 | 12 | suspend fun test() { 13 | delay(1000) 14 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/Sieve.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | val primes: Sequence = sequence { 4 | TODO() 5 | } 6 | 7 | // TODO: Delete it and replace it with sequence builder (above) 8 | fun getPrimeNumbers(num: Int): List { 9 | var numbers = generateSequence(2) { it + 1 } 10 | val primes = mutableListOf() 11 | repeat(num) { 12 | val prime = numbers.first() 13 | primes += prime 14 | numbers = numbers.drop(1).filter { it % prime != 0 } 15 | } 16 | return primes 17 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/Structured.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | import kotlinx.coroutines.runBlocking 4 | import java.util.* 5 | 6 | // We have a worker who makes machines every 800ms as long as there is less than 5 of them 7 | // Every machine produces a code using `produce` function every second. It saves this code to shared space. In case of an error, it ends working. 8 | // We have a single manager that once a 2 seconds takes all produced codes and prints them all. After 5 times it ends all jobs (including machines and worker). 9 | 10 | fun main() = runBlocking { 11 | TODO() 12 | } 13 | 14 | class RandomError : Throwable() 15 | 16 | private val letters = ('a'..'z') + ('0'..'9') 17 | private val random = Random() 18 | 19 | private fun produce(): String = when (random.nextInt(8)) { 20 | 0 -> throw RandomError() 21 | else -> (1..5).map { letters[random.nextInt(letters.size)] }.joinToString(separator = "") 22 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/backend/Api.kt: -------------------------------------------------------------------------------- 1 | package coroutines.backend 2 | 3 | import kotlinx.coroutines.runBlocking 4 | 5 | data class User(val name: String) 6 | 7 | fun main() { 8 | val database = Database() 9 | val emailService = EmailService() 10 | api { 11 | get("users") { 12 | "[]" 13 | } 14 | post("user") { body -> 15 | val user = body as? User ?: throw Error("Passed user is not correct") 16 | print("I just get $user") 17 | "OK" 18 | } 19 | get("user/count") { 20 | 0 21 | } 22 | }.start() 23 | 24 | runBlocking { 25 | println("User count is ${get("user/count")}") 26 | println("Users are ${get("users")}") 27 | post("user", User("Marcin")) 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/backend/ApiDsl.kt: -------------------------------------------------------------------------------- 1 | package coroutines.backend 2 | 3 | fun api(config: ApiConfig.() -> Unit) = ApiConfig().also(config) 4 | 5 | var publicHandles = mapOf Any>() 6 | 7 | data class Endpoint(val method: String, val path: String) 8 | 9 | class ApiConfig { 10 | var handles = mapOf Any>() 11 | 12 | fun get(path: String, handle: suspend (body: Any?) -> Any) { 13 | addHandle("get", path, handle) 14 | } 15 | 16 | fun post(path: String, handle: suspend (body: Any?) -> Any) { 17 | addHandle("post", path, handle) 18 | } 19 | 20 | fun start() { 21 | publicHandles += handles 22 | } 23 | 24 | private fun addHandle(method: String, path: String, handle: suspend (body: Any?) -> Any) { 25 | handles += Endpoint(method, path) to handle 26 | } 27 | } 28 | 29 | suspend fun get(path: String, body: Any? = null) = respond("get", path, body) 30 | 31 | suspend fun post(path: String, body: Any? = null) = respond("post", path, body) 32 | 33 | suspend fun respond(method: String, path: String, body: Any?): Any? { 34 | val handle = publicHandles[Endpoint(method, path)] ?: throw NoSuchMethodError() 35 | return try { 36 | handle(body) 37 | } catch (e: Throwable) { 38 | e.printStackTrace() 39 | null 40 | } 41 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/backend/Database.kt: -------------------------------------------------------------------------------- 1 | package coroutines.backend 2 | 3 | class Database { 4 | 5 | suspend fun getUser() {} 6 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/backend/EmailService.kt: -------------------------------------------------------------------------------- 1 | package coroutines.backend 2 | 3 | import kotlinx.coroutines.delay 4 | 5 | class EmailService { 6 | 7 | suspend fun sendEmail(to: String, body: String) { 8 | delay(2000) 9 | print("Sent email to $to with body $body") 10 | } 11 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/continuation/ContinuationSteal.kt: -------------------------------------------------------------------------------- 1 | package continuation 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.Continuation 5 | import kotlin.coroutines.resume 6 | 7 | fun main(): Unit = runBlocking { 8 | val cont = continuationSteal() 9 | delay(1000) 10 | cont?.resume("This is some text") 11 | } 12 | 13 | fun continuationSteal(console: Console = Console()): Continuation? = runBlocking { 14 | var continuation: Continuation? = null 15 | GlobalScope.launch(Dispatchers.Unconfined) { 16 | console.println("Before") 17 | TODO() 18 | console.println("After") 19 | } 20 | continuation 21 | } 22 | 23 | open class Console { 24 | 25 | open fun println(text: Any?) { 26 | kotlin.io.println(text) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/1.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | 7 | fun main() { 8 | GlobalScope.launch { 9 | delay(1000L) 10 | println("World!") 11 | } 12 | println("Hello,") 13 | Thread.sleep(2000L) 14 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/10.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | 8 | fun main() = runBlocking { 9 | launch { 10 | delay(200L) 11 | println("Task from runBlocking") 12 | } 13 | 14 | coroutineScope { 15 | launch { 16 | delay(500L) 17 | println("Task from nested launch") 18 | } 19 | 20 | delay(100L) 21 | println("Task from coroutine scope") 22 | } 23 | 24 | println("Coroutine scope is over") 25 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/11.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.async 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.runBlocking 6 | 7 | suspend fun makeAsyncCalculationsInCoroutineScope(): String = coroutineScope { 8 | val one = async { doSomethingUsefulOne() } 9 | val two = async { doSomethingUsefulTwo() } 10 | "The answer is ${one.await() + two.await()}" 11 | } 12 | 13 | fun main() = runBlocking { 14 | val value = makeAsyncCalculationsInCoroutineScope() 15 | println(value) 16 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/12.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.async 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.runBlocking 7 | 8 | suspend fun failedConcurrentSum(): Int = coroutineScope { 9 | val one = async { 10 | try { 11 | delay(Long.MAX_VALUE) 12 | 42 13 | } finally { 14 | println("First child was cancelled") 15 | } 16 | } 17 | val two = async { 18 | println("2nd child throws an exception") 19 | throw ArithmeticException() 20 | } 21 | one.await() + two.await() 22 | } 23 | 24 | fun main() = runBlocking { 25 | try { 26 | failedConcurrentSum() 27 | } catch (e: ArithmeticException) { 28 | println("Computation failed with ArithmeticException") 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/13.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.runBlocking 6 | 7 | fun main() = runBlocking { 8 | val deferred = GlobalScope.async { 9 | println("Throwing exception from async") 10 | throw ArithmeticException() 11 | } 12 | try { 13 | deferred.await() 14 | println("Unreached") 15 | } catch (e: ArithmeticException) { 16 | println("Caught ArithmeticException") 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/14.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | val supervisor = SupervisorJob() 7 | with(CoroutineScope(coroutineContext + supervisor)) { 8 | launch(CoroutineExceptionHandler { _, _ -> }) { 9 | delay(1000) 10 | throw AssertionError("Cancelled") 11 | } 12 | launch { 13 | delay(2000) 14 | println("AAA") 15 | } 16 | } 17 | supervisor.join() 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/15.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun main() = runBlocking { 6 | supervisorScope { 7 | launch(CoroutineExceptionHandler { _, _ -> }) { 8 | delay(1000) 9 | throw AssertionError("Cancelled") 10 | } 11 | launch { 12 | delay(2000) 13 | println("AAA") 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/2.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | 8 | fun main() { 9 | GlobalScope.launch { 10 | delay(1000L) 11 | println("World!") 12 | } 13 | println("Hello,") 14 | runBlocking { 15 | delay(2000L) 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/3.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | 8 | fun main() = runBlocking { 9 | GlobalScope.launch { 10 | delay(1000L) 11 | println("World!") 12 | } 13 | println("Hello,") 14 | delay(2000L) 15 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/4.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.* 4 | 5 | fun CoroutineScope.log(msg: String) = println("[${coroutineContext[CoroutineName]?.name}] $msg") 6 | 7 | fun main() = runBlocking(CoroutineName("main")) { 8 | log("Started main coroutine") 9 | val v1 = async(CoroutineName("v1coroutine")) { 10 | delay(500) 11 | log("Computing v1") 12 | "KOKO" 13 | } 14 | val v2 = async(CoroutineName("v2coroutine")) { 15 | delay(1000) 16 | log("Computing v2") 17 | 6 18 | } 19 | log("The answer for v1 = ${v1.await()}") 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/5.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.Job 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | 8 | fun main() = runBlocking { 9 | val job = Job() 10 | launch(job) { 11 | repeat(1000) { i -> 12 | println("I'm sleeping $i ...") 13 | delay(500L) 14 | } 15 | } 16 | delay(1300L) // delay a bit 17 | println("main: I'm tired of waiting!") 18 | job.cancel() 19 | job.join() 20 | println("main: Now I can quit.") 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/6.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.runBlocking 6 | 7 | fun main() = runBlocking { 8 | val job = launch { 9 | repeat(1000) { i -> 10 | println("I'm sleeping $i ...") 11 | delay(500L) 12 | } 13 | } 14 | delay(1300L) // delay a bit 15 | println("main: I'm tired of waiting!") 16 | job.cancel() 17 | job.join() 18 | println("main: Now I can quit.") 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/7.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.CoroutineExceptionHandler 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | 8 | fun main() = runBlocking { 9 | val handler = CoroutineExceptionHandler { _, exception -> 10 | println("Caught $exception") 11 | } 12 | val job = GlobalScope.launch(handler) { 13 | throw AssertionError() 14 | } 15 | job.join() // Caught java.lang.AssertionError 16 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/8.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.newSingleThreadContext 6 | import kotlinx.coroutines.runBlocking 7 | 8 | fun main() = runBlocking { 9 | fun getThreadName() = Thread.currentThread().name 10 | launch { 11 | println("main runBlocking : I'm working in thread ${getThreadName()}") 12 | } 13 | launch(Dispatchers.Default) { 14 | println("Default : I'm working in thread ${getThreadName()}") 15 | launch(Dispatchers.Unconfined) { 16 | println("Unconfined : I'm working in thread ${getThreadName()}") 17 | } 18 | } 19 | 20 | launch(newSingleThreadContext("MyOwnThread")) { 21 | println("newSingleThreadContext: I'm working in thread ${getThreadName()}") 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/9.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.runBlocking 7 | import java.util.* 8 | 9 | suspend fun makeAsyncCalculations(): String { 10 | val one = GlobalScope.async { doSomethingUsefulOne() } 11 | val two = GlobalScope.async { doSomethingUsefulTwo() } 12 | return "The answer is ${one.await() + two.await()}" 13 | } 14 | 15 | suspend fun doSomethingUsefulOne(): Int { 16 | delay(1000) 17 | println("I am done") 18 | return 1 19 | } 20 | 21 | val random = Random() 22 | 23 | suspend fun doSomethingUsefulTwo(): Int { 24 | delay(100) 25 | if (random.nextBoolean()) throw Error() else return 2 26 | } 27 | 28 | fun main() = runBlocking { 29 | val value = makeAsyncCalculations() 30 | println(value) 31 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/Delay.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import java.util.concurrent.Executors 4 | import java.util.concurrent.TimeUnit 5 | import kotlin.coroutines.resume 6 | import kotlin.coroutines.suspendCoroutine 7 | 8 | private val excecutor = Executors.newSingleThreadScheduledExecutor { 9 | Thread(it, "scheduler").apply { isDaemon = true } 10 | } 11 | 12 | suspend fun customDelay(time: Long): Unit = suspendCoroutine { cont -> 13 | excecutor.schedule({ cont.resume(Unit) }, time, TimeUnit.MILLISECONDS) 14 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/Massive.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.runBlocking 6 | 7 | fun main() = runBlocking { 8 | repeat(100_000) { 9 | launch { 10 | delay(1000L) 11 | print(".") 12 | } 13 | } 14 | } 15 | 16 | //// No! Don't do it! Very bed idea on threads 17 | //fun main() { 18 | // repeat(100_000) { 19 | // thread { 20 | // Thread.sleep(1000L) 21 | // print(".") 22 | // } 23 | // } 24 | //} -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/Numbers.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | val childNumbers = sequence { 4 | println("Um, first number is... one!") 5 | yield(1) 6 | 7 | println("Next is... eeeee two!") 8 | yield(2) 9 | 10 | println("twotwotwo... ummmm three!") 11 | yield(3) 12 | 13 | println("That's all I've learned") 14 | } 15 | 16 | fun main() { 17 | val iterator = childNumbers.iterator() 18 | println("What is first?") 19 | println("Yes, it is ${iterator.next()}") 20 | println("What is next?") 21 | println("Good, it is ${iterator.next()}") 22 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/s1.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | 8 | var counter = 0 9 | 10 | fun main() = runBlocking { 11 | GlobalScope.massiveRun { 12 | counter++ 13 | } 14 | println("Counter = ${counter}") 15 | } 16 | 17 | suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) { 18 | val jobs = List(1000) { 19 | launch { 20 | repeat(1000) { action() } 21 | } 22 | } 23 | jobs.forEach { it.join() } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/s2.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples.n1 2 | 3 | import coroutines.examples.massiveRun 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.runBlocking 6 | import java.util.concurrent.atomic.AtomicInteger 7 | 8 | private var counter = AtomicInteger() 9 | 10 | fun main() = runBlocking { 11 | GlobalScope.massiveRun { 12 | counter.incrementAndGet() 13 | } 14 | println("Counter = ${counter.get()}") 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/s3.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples.n2 2 | 3 | import coroutines.examples.massiveRun 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.newSingleThreadContext 6 | import kotlinx.coroutines.runBlocking 7 | import kotlinx.coroutines.withContext 8 | 9 | private var counter = 0 10 | 11 | fun main() = runBlocking { 12 | val counterContext = newSingleThreadContext("CounterContext") 13 | 14 | GlobalScope.massiveRun { 15 | withContext(counterContext) { 16 | counter++ 17 | } 18 | } 19 | println("Counter = $counter") 20 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/examples/s4.kt: -------------------------------------------------------------------------------- 1 | package coroutines.examples.n3 2 | 3 | import coroutines.examples.massiveRun 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.runBlocking 6 | import kotlinx.coroutines.sync.Mutex 7 | import kotlinx.coroutines.sync.withLock 8 | 9 | private val mutex = Mutex() 10 | private var counter = 0 11 | 12 | fun main() = runBlocking { 13 | GlobalScope.massiveRun { 14 | mutex.withLock { 15 | counter++ 16 | } 17 | } 18 | println("Counter = $counter") 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/corotuines/request/Request.kt: -------------------------------------------------------------------------------- 1 | package corotuines.request 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.coroutineScope 6 | 7 | /* 8 | TODO: This function should return the best student on the [semester]. 9 | */ 10 | suspend fun getBestStudent(semester: String, repo: StudentsRepository): Student = TODO() 11 | 12 | data class Student(val id: Int, val result: Double, val semester: String) 13 | 14 | interface StudentsRepository { 15 | suspend fun getStudentIds(semester: String): List 16 | suspend fun getStudent(id: Int): Student 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/corotuines/ui/BasePresenter.kt: -------------------------------------------------------------------------------- 1 | package coroutines.ui 2 | 3 | import kotlinx.coroutines.* 4 | 5 | val UI = newSingleThreadContext("UIThread") // Normally it will be Dispatchers.Main 6 | 7 | // TODO: Edit only this class 8 | abstract class BasePresenter( 9 | private val onError: (Throwable) -> Unit = {} 10 | ) { 11 | 12 | fun onDestroy() {} 13 | } -------------------------------------------------------------------------------- /src/main/java/corotuines/ui/MainPresenter.kt: -------------------------------------------------------------------------------- 1 | package coroutines.ui 2 | 3 | import kotlinx.coroutines.launch 4 | import java.util.* 5 | 6 | class MainPresenter( 7 | val view: MainView, 8 | val repo: NetworkRepository 9 | ) : BasePresenter(view::onError) { 10 | 11 | fun onCreate() { 12 | // TODO: Uncomment 13 | // launch { 14 | // val user = repo.getUser() 15 | // view.showUserData(user) 16 | // } 17 | // launch { 18 | // val news = repo.getNews() 19 | // .sortedByDescending { it.date } 20 | // view.showNews(news) 21 | // } 22 | } 23 | } 24 | 25 | interface MainView { 26 | fun onError(throwable: Throwable): Unit 27 | fun showUserData(user: UserData) 28 | fun showNews(news: List) 29 | } 30 | 31 | interface NetworkRepository { 32 | suspend fun getUser(): UserData 33 | suspend fun getNews(): List 34 | } 35 | 36 | class UserData 37 | class News(val date: Date) -------------------------------------------------------------------------------- /src/main/java/delegates/MutableLazy.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("Mutable") 2 | package delegates 3 | 4 | import kotlin.properties.ReadWriteProperty 5 | 6 | fun mutableLazy(initializer: () -> T): ReadWriteProperty = TODO() -------------------------------------------------------------------------------- /src/main/java/dsl/Announcements.kt: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | fun getAnnouncements(passingStudentsListText: String, bestStudentsListText: String): List = TODO() 4 | // announcements { 5 | // reminder("If you want to receive internship, you need to provide documents till end of September") 6 | // info { 7 | // title = "Students who are passing:" 8 | // content = passingStudentsListText 9 | // } 10 | // info { 11 | // title = "Internships:" 12 | // content = bestStudentsListText 13 | // } 14 | // reminder("Work hard whole year and prepare to all classes") 15 | // info { 16 | // content = "Checking this app periodically will help you be up to date with your university" 17 | // } 18 | // } 19 | 20 | data class Announcement( 21 | val title: String?, 22 | val text: String 23 | ) -------------------------------------------------------------------------------- /src/main/java/dsl/TableDSL.kt: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | fun createTable(): TableBuilder { 4 | // TODO: This is how I would prefer to print it 5 | //return table { 6 | // tr { 7 | // td { +"A" } 8 | // td { +"B" } 9 | // } 10 | // tr { 11 | // td { +"C" } 12 | // td { +"D" } 13 | // } 14 | //} 15 | val td1 = TdBuilder() 16 | td1.text = "A" 17 | val td2 = TdBuilder() 18 | td2.text = "B" 19 | 20 | val tr1 = TrBuilder() 21 | tr1.tds += td1 22 | tr1.tds += td2 23 | 24 | val td3 = TdBuilder() 25 | td3.text = "C" 26 | val td4 = TdBuilder() 27 | td4.text = "D" 28 | 29 | val tr2 = TrBuilder() 30 | tr2.tds += td3 31 | tr2.tds += td4 32 | 33 | val html = TableBuilder() 34 | html.trs += tr1 35 | html.trs += tr2 36 | return html 37 | } 38 | 39 | fun main() { 40 | println(createTable()) //
This is row 1This is row 2
41 | } 42 | 43 | data class TableBuilder(var trs: List = emptyList()) { 44 | override fun toString(): String = "${trs.joinToString(separator = "")}
" 45 | } 46 | data class TrBuilder(var tds: List = emptyList()) { 47 | override fun toString(): String = "${tds.joinToString(separator = "")}" 48 | } 49 | data class TdBuilder(var text: String = "") { 50 | override fun toString(): String = "$text" 51 | } -------------------------------------------------------------------------------- /src/main/java/dsl/UsersTable.kt: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | data class User(val id: String, val name: String, val points: Int, val category: String) 4 | 5 | fun usersTable(users: List): TableBuilder = TODO() 6 | -------------------------------------------------------------------------------- /src/main/java/extra/Permutations.kt: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | /** 4 | * Permutations are all different ways to arrange elements from some collection (https://en.wikipedia.org/wiki/Permutation). 5 | * For sets it's number is n!, for lists it is n! / (n1! * n2! * ...) where n1, n2... are numbers elements that are the same. 6 | */ 7 | 8 | /* This function returns number of all permutations of elements from set. It is equal to n! where n is size of set. */ 9 | fun Set.permutationsNumber(): Long = TODO() 10 | 11 | /* This function returns number of all permutations of elements from list. It is equal to n! / (n1! * n2! * ...) where n1, n2... are numbers elements that are the same. */ 12 | fun List.permutationsNumber(): Long = TODO() 13 | 14 | /* This function returns all permutations of elements from set. These are different ways to arrange elements from this list. */ 15 | fun Set.permutations(): Set> = TODO() 16 | 17 | /* This function returns all permutations of elements from list. These are different ways to arrange elements from this list. */ 18 | fun List.permutations(): Set> = TODO() -------------------------------------------------------------------------------- /src/main/java/extra/Powerset.kt: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | // Powerset returns set of all subsets including full set and empty set 4 | // https://en.wikipedia.org/wiki/Power_set 5 | fun Collection.powerset(): Set> = TODO() -------------------------------------------------------------------------------- /src/main/java/functional/Callbacks.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | import basics.ApiError 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties 5 | import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper 6 | import okhttp3.OkHttpClient 7 | import retrofit2.Call 8 | import retrofit2.Callback 9 | import retrofit2.Response 10 | import retrofit2.Retrofit 11 | import retrofit2.converter.jackson.JacksonConverterFactory 12 | import retrofit2.http.GET 13 | import retrofit2.http.Path 14 | import java.util.* 15 | 16 | fun main() { 17 | val username = "" 18 | val token = "" 19 | val service: GitHubService = createGitHubService(username, token) 20 | 21 | getAggregatedContributions(service) { users -> 22 | val sortedUsers = users.sortedByDescending { it.contributions } 23 | println("Aggregated contributions:") 24 | for ((index, user) in sortedUsers.withIndex()) { 25 | println("$index: ${user.login} with ${user.contributions} contributions") 26 | } 27 | } 28 | } 29 | 30 | fun getAggregatedContributions(service: GitHubService, callback: (List) -> Unit) { 31 | TODO() 32 | } 33 | 34 | interface GitHubService { 35 | fun getOrgRepos(callback: (List) -> Unit) 36 | fun getRepoContributors(repo: String, callback: (List) -> Unit) 37 | } 38 | 39 | fun createGitHubService(username: String, password: String): GitHubService { 40 | val authToken = "Basic " + Base64.getEncoder().encode("$username:$password".toByteArray()).toString(Charsets.UTF_8) 41 | val httpClient = OkHttpClient.Builder() 42 | .addInterceptor { chain -> 43 | val original = chain.request() 44 | val builder = original.newBuilder() 45 | .header("Accept", "application/vnd.github.v3+json") 46 | .header("Authorization", authToken) 47 | val request = builder.build() 48 | chain.proceed(request) 49 | } 50 | .build() 51 | 52 | return Retrofit.Builder() 53 | .baseUrl("https://api.github.com") 54 | .addConverterFactory(JacksonConverterFactory.create(jacksonObjectMapper())) 55 | .client(httpClient) 56 | .build() 57 | .create(GitHubServiceApiDef::class.java) 58 | .let(::GitHubServiceImpl) 59 | } 60 | 61 | class GitHubServiceImpl(val apiService: GitHubServiceApiDef) : GitHubService { 62 | override fun getOrgRepos(callback: (List) -> Unit) = 63 | apiService.getOrgReposCall().onResponse { 64 | callback(it.body() ?: throw ApiError(it.code(), it.message())) 65 | } 66 | 67 | override fun getRepoContributors(repo: String, callback: (List) -> Unit) = 68 | apiService.getRepoContributorsCall(repo).onResponse { 69 | callback(it.body() ?: throw ApiError(it.code(), it.message())) 70 | } 71 | } 72 | 73 | interface GitHubServiceApiDef { 74 | @GET("orgs/jetbrains/repos?per_page=100") 75 | fun getOrgReposCall(): Call> 76 | 77 | @GET("repos/jetbrains/{repo}/contributors?per_page=100") 78 | fun getRepoContributorsCall( 79 | @Path("repo") repo: String 80 | ): Call> 81 | } 82 | 83 | @JsonIgnoreProperties(ignoreUnknown = true) 84 | data class Repo( 85 | val id: Long, 86 | val name: String 87 | ) 88 | 89 | @JsonIgnoreProperties(ignoreUnknown = true) 90 | data class User( 91 | val login: String, 92 | val contributions: Int 93 | ) 94 | 95 | inline fun Call.onResponse(crossinline callback: (Response) -> Unit) { 96 | enqueue(object : Callback { 97 | override fun onResponse(call: Call, response: Response) { 98 | callback(response) 99 | } 100 | 101 | override fun onFailure(call: Call, t: Throwable) { 102 | throw t 103 | } 104 | }) 105 | } -------------------------------------------------------------------------------- /src/main/java/functional/Functional.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | class FunctionsClassic { 4 | 5 | fun add(num1: Int, num2: Int): Int = num1 + num2 6 | 7 | fun printNum(num: Int) { 8 | print(num) 9 | } 10 | 11 | fun triple(num: Int): Int = num * 3 12 | 13 | fun longestOf(str1: String, str2: String, str3: String): String = listOf(str1, str2, str3) 14 | .maxByOrNull { it.length }!! 15 | } 16 | 17 | interface FunctionsFunctional { 18 | val add: Any 19 | val printNum: Any 20 | val triple: Any 21 | val longestOf: Any 22 | } 23 | 24 | class AnonymousFunctionalTypeSpecified : FunctionsFunctional { 25 | override val add: (Int, Int) -> Int = fun(num1, num2) = num1 + num2 26 | override val printNum = TODO() 27 | override val triple = TODO() 28 | override val longestOf = TODO() 29 | } 30 | 31 | class AnonymousFunctionalTypeInferred : FunctionsFunctional { 32 | override val add = fun(num1: Int, num2: Int) = num1 + num2 33 | override val printNum = TODO() 34 | override val triple = TODO() 35 | override val longestOf = TODO() 36 | } 37 | 38 | class LambdaFunctionalTypeSpecified : FunctionsFunctional { 39 | override val add: (Int, Int) -> Int = { num1, num2 -> num1 + num2 } 40 | override val printNum = TODO() 41 | override val triple = TODO() 42 | override val longestOf = TODO() 43 | } 44 | 45 | class LambdaFunctionalTypeInferred : FunctionsFunctional { 46 | override val add = { num1: Int, num2: Int -> num1 + num2 } 47 | override val printNum = TODO() 48 | override val triple = TODO() 49 | override val longestOf = TODO() 50 | } 51 | 52 | class FunctionReference : FunctionsFunctional { 53 | override val add: (Int, Int) -> Int = Int::plus 54 | override val printNum: (Int) -> Unit = TODO() 55 | override val triple: (Int) -> Int = TODO() 56 | override val longestOf: (String, String, String) -> String = TODO() 57 | } 58 | 59 | class FunctionMemberReference : FunctionsFunctional { 60 | override val add: (Int, Int) -> Int = this::add 61 | override val printNum: (Int) -> Unit = TODO() 62 | override val triple: (Int) -> Int = TODO() 63 | override val longestOf: (String, String, String) -> String = TODO() 64 | 65 | private fun add(num1: Int, num2: Int): Int = num1 + num2 66 | 67 | private fun printNum(num: Int) { 68 | print(num) 69 | } 70 | 71 | private fun triple(num: Int): Int = num * 3 72 | 73 | private fun longestOf(str1: String, str2: String, str3: String): String = listOf(str1, str2, str3) 74 | .maxByOrNull { it.length }!! 75 | } 76 | 77 | class BoundedFunctionReference : FunctionsFunctional { 78 | private val classic = FunctionsClassic() 79 | 80 | override val add: (Int, Int) -> Int = classic::add 81 | override val printNum: (Int) -> Unit = TODO() 82 | override val triple: (Int) -> Int = TODO() 83 | override val longestOf: (String, String, String) -> String = TODO() 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/functional/Product.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | fun Iterable.product(): Long = TODO() -------------------------------------------------------------------------------- /src/main/java/functional/Rational.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | data class Rational(val numerator: Int, val denominator: Int) 4 | 5 | fun Int.r(): Rational = TODO() 6 | fun Pair.r(): Rational = TODO() 7 | 8 | fun main(args: Array) { 9 | print(1.r()) 10 | print((1 to 2).r()) 11 | } -------------------------------------------------------------------------------- /src/main/java/functional/References.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | fun zeroComplex(): Complex = Complex(0.0, 0.0) 4 | fun makeComplex(real: Double = 0.0, imaginary: Double = 0.0): Complex = Complex(real, imaginary) 5 | 6 | data class Complex(var real: Double, val imaginary: Double) { 7 | fun doubled(): Complex = Complex(this.real * 2, this.imaginary * 2) 8 | fun times(num: Int) = Complex(real * num, imaginary * num) 9 | } 10 | 11 | fun Complex.plus(other: Complex): Complex = Complex(real + other.real, imaginary + other.imaginary) 12 | fun Int.toComplex(): Complex = Complex(this.toDouble(), 0.0) 13 | 14 | fun produceAndPrint(producer: () -> Complex) { 15 | println(producer()) 16 | } 17 | 18 | fun main() { 19 | val c0: Complex = zeroComplex() 20 | val c1: Complex = makeComplex(1.0, 2.0) 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/functional/Repeat.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | fun repeat(times: Int, action: () -> Unit) { 4 | 5 | } 6 | 7 | fun main(args: Array) { 8 | repeat(5) { print("A") } // AAAAA 9 | 10 | // var i = 1 11 | // loop { 12 | // print("A") 13 | // i *= 2 14 | // if(i > 1000) { 15 | // // break 16 | // } 17 | // } 18 | } -------------------------------------------------------------------------------- /src/main/java/functional/UserDocument.kt: -------------------------------------------------------------------------------- 1 | package functional.document 2 | 3 | class User( 4 | val id: String, 5 | val name: String, 6 | val surname: String, 7 | val age: Int, 8 | val tokens: List 9 | ) 10 | 11 | class UserDocument( 12 | val userId: String, 13 | val firstName: String, 14 | val lastName: String, 15 | val age: Int, 16 | val tokens: List 17 | ) 18 | 19 | fun User.toUserDocument(): UserDocument = TODO() 20 | 21 | fun UserDocument.toUser(): User = TODO() -------------------------------------------------------------------------------- /src/main/java/generics/Consumer.kt: -------------------------------------------------------------------------------- 1 | abstract class Consumer { 2 | abstract fun consume(elem: T) 3 | } 4 | 5 | class Printer : Consumer() { 6 | private var toPrint: T? = null 7 | 8 | fun print() { 9 | println("Printing $toPrint") 10 | } 11 | 12 | override fun consume(elem: T) { 13 | toPrint = elem 14 | } 15 | } 16 | 17 | class Sender : Consumer() { 18 | override fun consume(elem: T) { 19 | // ... 20 | } 21 | } 22 | 23 | fun getConsumer(): Consumer = Printer() 24 | 25 | fun sendInt(sender: Sender) {} 26 | fun sendFloat(sender: Sender) {} 27 | 28 | fun main(args: Array) { 29 | val consumer = getConsumer() 30 | consumer.consume(10) 31 | 32 | val sender = Sender() 33 | // sendInt(sender) 34 | // sendFloat(sender) 35 | // val c1: Consumer = Printer() 36 | // val c2: Consumer = Sender() 37 | // val c3: Printer = Printer() 38 | // val c4: Sender = Sender() 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/generics/Generics.kt: -------------------------------------------------------------------------------- 1 | package generics 2 | 3 | class Box(e: T) { 4 | var e: T = e 5 | fun get(): T = e 6 | fun put(e: T) { 7 | this.e = e 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/generics/Response.kt: -------------------------------------------------------------------------------- 1 | package generics 2 | 3 | sealed class Response 4 | class Success(val value: R) : Response() 5 | class Failure(val error: E) : Response() 6 | 7 | fun processResponseInt(response: Response) { /*...*/ 8 | } 9 | 10 | fun processResponseString(response: Response) { /*...*/ 11 | } 12 | 13 | //fun usage() { 14 | // processResponseInt(Success(1)) 15 | // processResponseInt(Failure("ERROR")) 16 | // processResponseString(Success("ERROR")) 17 | // processResponseString(Failure(Error("ERROR"))) 18 | // 19 | // val rs1 = Success(1) 20 | // val re1 = Failure(Error()) 21 | // val re2 = Failure("Error") 22 | // 23 | // val rs1asNumber: Success = rs1 24 | // val rs1asAny: Success = rs1 25 | // 26 | // val re1asThrowable: Failure = re1 27 | // val re1asAny: Failure = re1 28 | // 29 | // val r1: Response = rs1 30 | // val r2: Response = re1 31 | // 32 | // val r3: Response = rs1 33 | // val r4: Response = re2 34 | // 35 | // val r5: Response = rs1 36 | // val r6: Response = re1 37 | // 38 | // val s = Success(String()) 39 | // val s1: Success = s 40 | // val s2: Success = s 41 | // 42 | // val e = Failure(Error()) 43 | // val e1: Failure = e 44 | // val e2: Failure = e 45 | //} 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/javatask/JavaClass.java: -------------------------------------------------------------------------------- 1 | package javatask; 2 | 3 | public class JavaClass { 4 | 5 | public static void main(String[] args) { 6 | // KotlinClass.staticFunction(); 7 | // KotlinPerson person = new KotlinPerson("Marcin"); 8 | // KotlinPerson person2 = new KotlinPerson(); 9 | // KotlinTopLevel.topLevelFunction(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/javatask/JavaPerson.java: -------------------------------------------------------------------------------- 1 | package javatask; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public class JavaPerson { 6 | 7 | @NotNull 8 | private String name; 9 | private int age; 10 | 11 | public JavaPerson(@NotNull String name, int age) { 12 | super(); 13 | this.name = name; 14 | this.age = age; 15 | } 16 | 17 | public final boolean isMature() { 18 | return this.age > 18; 19 | } 20 | 21 | @NotNull 22 | public final String getName() { 23 | return this.name; 24 | } 25 | 26 | public final void setName(@NotNull String name) { 27 | this.name = name; 28 | } 29 | 30 | public final int getAge() { 31 | return this.age; 32 | } 33 | 34 | public final void setAge(int var1) { 35 | this.age = var1; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/javatask/KotlinClass.kt: -------------------------------------------------------------------------------- 1 | package javatask 2 | 3 | class KotlinClass { 4 | companion object { 5 | fun staticFunction() { 6 | print("This is staticFunction") 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/main/java/javatask/KotlinPerson.kt: -------------------------------------------------------------------------------- 1 | package javatask 2 | 3 | class KotlinPerson( 4 | var name: String = "", 5 | var age: Int = -1 6 | ) { 7 | val isMature: Boolean 8 | get() = age > 18 9 | } -------------------------------------------------------------------------------- /src/main/java/javatask/TopLevel.kt: -------------------------------------------------------------------------------- 1 | package javatask 2 | 3 | fun topLevelFunction() { 4 | print("This is topLevelFunction") 5 | } -------------------------------------------------------------------------------- /src/main/java/nullability/MessageUtil.java: -------------------------------------------------------------------------------- 1 | package nullability; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public class MessageUtil { 7 | public static void sendMessageToClient(@Nullable Client client, @Nullable String message, @NotNull Mailer mailer) { 8 | if (client == null || message == null) return; 9 | 10 | PersonalInfo personalInfo = client.getPersonalInfo(); 11 | if (personalInfo == null) return; 12 | 13 | String email = personalInfo.getEmail(); 14 | if (email == null) return; 15 | 16 | mailer.sendMessage(email, message); 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/java/nullability/Task.kt: -------------------------------------------------------------------------------- 1 | package nullability 2 | 3 | class Client(val personalInfo: PersonalInfo?) 4 | class PersonalInfo(val email: String?) 5 | 6 | interface Mailer { 7 | fun sendMessage(email: String, message: String) 8 | } 9 | 10 | // 1. Use Elvis operator for unpacking both values 11 | // 2. Use Elvis operator for unpacking email and smart-casting message 12 | // 3. Use Elvis operator smart-casting email and message 13 | // 4. Use if for smart-casting email and message 14 | // 5. Use if with return for smart-casting email and message 15 | // Optional: 6. Use let for email and message 16 | 17 | /* 18 | Send message if the message, client, personal info and email are not null. 19 | */ 20 | fun sendMessageToClient(client: Client?, message: String?, mailer: Mailer) { 21 | 22 | } -------------------------------------------------------------------------------- /src/main/java/operators/Money.kt: -------------------------------------------------------------------------------- 1 | package operators 2 | 3 | import java.math.BigDecimal 4 | 5 | enum class Currency { EUR, PLN, GBP } 6 | 7 | data class Money(val amount: BigDecimal, val currency: Currency) { 8 | } 9 | 10 | fun main() { 11 | val money1 = Money(10.toBigDecimal(), Currency.EUR) 12 | val money2 = Money(20.toBigDecimal(), Currency.EUR) 13 | 14 | // println(money1 + money2) 15 | } -------------------------------------------------------------------------------- /src/main/java/types/Relations.kt: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import kotlin.random.Random 4 | 5 | open class Animal 6 | class Dog : Animal() 7 | 8 | fun main() { 9 | val dog: Dog = Dog() 10 | val nullableDog: Dog? = if (Random.nextBoolean()) Dog() else null 11 | val animal: Animal = Animal() 12 | val nullableAnimal: Animal? = if (Random.nextBoolean()) Animal() else null 13 | } 14 | 15 | fun consumeDog(dog: Dog) {} 16 | fun consumeNullableDog(dog: Dog?) {} 17 | fun consumeAnimal(animal: Animal) {} 18 | fun consumeNullableAnimal(animal: Animal?) {} 19 | fun consumeAny(any: Any) {} 20 | fun consumeNullableAny(any: Any?) {} 21 | fun consumeNothing(nothing: Any) {} 22 | fun consumeNullableNothing(nothing: Any?) {} 23 | -------------------------------------------------------------------------------- /src/main/java/types/Types.kt: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import kotlin.random.Random 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | fun main() { 41 | val a: Int = if (Random.nextBoolean()) 42 else 10 42 | 43 | val message: String = produceMessage() ?: "ABC" 44 | 45 | val grade: Char = when (result) { 46 | in 0 until 50 -> 'F' 47 | in 50 until 65 -> 'D' 48 | in 65 until 80 -> 'C' 49 | in 80 until 90 -> 'B' 50 | in 90..100 -> 'A' 51 | else -> 'X' 52 | } 53 | } 54 | 55 | val result = Random.nextInt(1, 100) 56 | fun produceMessage(): String? = null 57 | -------------------------------------------------------------------------------- /src/test/java/BasicsTest.kt: -------------------------------------------------------------------------------- 1 | import org.junit.Test 2 | import kotlin.test.assertEquals 3 | import kotlin.test.assertTrue 4 | 5 | @Suppress("FunctionName") 6 | internal class BasicsTest { 7 | 8 | @Test 9 | fun `gcd returnes x for x and x`() { 10 | assertEquals(5, gcd(5, 5)) 11 | assertEquals(7, gcd(7, 7)) 12 | for (x in 1..100) { 13 | assertEquals(x, gcd(x, x)) 14 | } 15 | } 16 | 17 | @Test 18 | fun `gcd returnes 1 when x and y are primes`() { 19 | assertEquals(1, gcd(3, 7)) 20 | assertEquals(1, gcd(5, 7)) 21 | 22 | for ((x, y) in twoDifferentPrimesPermutations) { 23 | assertEquals(1, gcd(x, y), "Should be 1 for $x and $y, but is ${gcd(x, y)}") 24 | } 25 | } 26 | 27 | @Test 28 | fun `gcd for simple examples`() { 29 | assertEquals(4, gcd(12, 8)) 30 | assertEquals(6, gcd(12, 6)) 31 | assertEquals(14, gcd(42, 56)) 32 | assertEquals(18, gcd(461952, 116298)) 33 | assertEquals(32, gcd(7966496, 314080416)) 34 | assertEquals(526, gcd(24826148, 45296490)) 35 | } 36 | 37 | @Test 38 | fun `gcd x and 0 gives x`() { 39 | assertEquals(12, gcd(12, 0)) 40 | assertEquals(0, gcd(0, 0)) 41 | assertEquals(9, gcd(0, 9)) 42 | } 43 | 44 | @Test 45 | fun `FizzBuzz prints 100 elements`() { 46 | val list = fuzzBuzzPrinted() 47 | assertEquals(100, list.size) 48 | } 49 | 50 | @Test 51 | fun `FizzBuzz starts from 1 and 2`() { 52 | val (first, second) = fuzzBuzzPrinted() 53 | assertEquals("1", first) 54 | assertEquals("2", second) 55 | } 56 | 57 | @Test 58 | fun `Third FizzBuzz element is Fizz`() { 59 | val (_, _, third) = fuzzBuzzPrinted() 60 | assertEquals("Fizz", third) 61 | } 62 | 63 | @Test 64 | fun `Fifth FizzBuzz element is Buzz`() { 65 | val (_, _, _, _, fifth) = fuzzBuzzPrinted() 66 | assertEquals("Buzz", fifth) 67 | } 68 | 69 | @Test 70 | fun `Fifteenth FizzBuzz element is FizzBuzz`() { 71 | val text = fuzzBuzzPrinted() 72 | assertEquals("FizzBuzz", text[14]) 73 | } 74 | 75 | @Test 76 | fun `Every third element returns Fizz`() { 77 | val text = fuzzBuzzPrinted() 78 | for (position in positionsForNumbersDividableBy(3)) { 79 | assertTrue("Fizz" in text[position]) 80 | } 81 | } 82 | 83 | @Test 84 | fun `Every fifth element returns Fizz`() { 85 | val text = fuzzBuzzPrinted() 86 | for (position in positionsForNumbersDividableBy(5)) { 87 | assertTrue("Buzz" in text[position]) 88 | } 89 | } 90 | 91 | @Test 92 | fun `Every fifteenth element returns FizzBuzz`() { 93 | val text = fuzzBuzzPrinted() 94 | for (position in positionsForNumbersDividableBy(15)) { 95 | assertEquals("FizzBuzz", text[position]) 96 | } 97 | } 98 | 99 | private fun positionsForNumbersDividableBy(num: Int) = 100 | (1..100).filter { it % num == 0 }.map { it - 1 } 101 | 102 | @Test 103 | fun fizzBuzzTest() { 104 | val text = fuzzBuzzPrinted() 105 | val solution = 106 | "1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n16\n17\nFizz\n19\nBuzz\nFizz\n22\n23\nFizz\nBuzz\n26\nFizz\n28\n29\nFizzBuzz\n31\n32\nFizz\n34\nBuzz\nFizz\n37\n38\nFizz\nBuzz\n41\nFizz\n43\n44\nFizzBuzz\n46\n47\nFizz\n49\nBuzz\nFizz\n52\n53\nFizz\nBuzz\n56\nFizz\n58\n59\nFizzBuzz\n61\n62\nFizz\n64\nBuzz\nFizz\n67\n68\nFizz\nBuzz\n71\nFizz\n73\n74\nFizzBuzz\n76\n77\nFizz\n79\nBuzz\nFizz\n82\n83\nFizz\nBuzz\n86\nFizz\n88\n89\nFizzBuzz\n91\n92\nFizz\n94\nBuzz\nFizz\n97\n98\nFizz\nBuzz" 107 | assertEquals(solution, text.joinToString(separator = "\n") { it.trim() }) 108 | } 109 | 110 | private fun fuzzBuzzPrinted(): List { 111 | val printedLines = mutableListOf() 112 | val printer = object : Console { 113 | override fun println(text: String) { 114 | printedLines += text 115 | } 116 | } 117 | fizzBuzz(printer) 118 | return printedLines 119 | } 120 | 121 | @Test 122 | fun `fib first numbers`() { 123 | assertEquals(1, fib(0)) 124 | assertEquals(1, fib(1)) 125 | assertEquals(2, fib(2)) 126 | assertEquals(3, fib(3)) 127 | assertEquals(5, fib(4)) 128 | assertEquals(8, fib(5)) 129 | assertEquals(13, fib(6)) 130 | assertEquals(21, fib(7)) 131 | assertEquals(34, fib(8)) 132 | assertEquals(55, fib(9)) 133 | assertEquals(89, fib(10)) 134 | } 135 | 136 | companion object { 137 | private val somePrimes = listOf(2, 3, 5, 7, 11, 13, 17, 19) 138 | 139 | private val twoDifferentPrimesPermutations = somePrimes 140 | .flatMap { p1 -> somePrimes.map { p2 -> p1 to p2 } } 141 | .filter { (p1, p2) -> p1 != p2 } 142 | } 143 | } -------------------------------------------------------------------------------- /src/test/java/SendMessageTest.kt: -------------------------------------------------------------------------------- 1 | import org.junit.After 2 | import org.junit.Test 3 | import sendmessage.Email 4 | import sendmessage.EmailAddress 5 | import sendmessage.MessageSender 6 | import sendmessage.MessageService 7 | import kotlin.reflect.full.functions 8 | import kotlin.reflect.typeOf 9 | import kotlin.test.assertEquals 10 | import kotlin.test.assertNotNull 11 | 12 | class SendMessageTest { 13 | private val sender = FakeMessageSender() 14 | private val messageService: MessageService = MessageService(sender) 15 | 16 | @After 17 | fun cleanup() { 18 | sender.clear() 19 | } 20 | 21 | @Test 22 | fun `function with correct name exist`() { 23 | val function = MessageService::class.functions.find { it.name == "sendMessage" } 24 | assertNotNull(function) { "You must define a function with name sendMessage" } 25 | } 26 | 27 | @Test 28 | fun `the function has 3 parameters (+ receiver)`() { 29 | val function = MessageService::class.functions.find { it.name == "sendMessage" } 30 | assertNotNull(function) { "You must define a function with name sendMessage" } 31 | assertEquals(4, function.parameters.size, "Function should have three parameters" ) 32 | } 33 | 34 | @Test 35 | fun `the function parameters has correct types`() { 36 | val function = MessageService::class.functions.find { it.name == "sendMessage" } 37 | assertNotNull(function) { "You must define a function with name sendMessage" } 38 | assertEquals(typeOf(), function.parameters[1].type) 39 | assertEquals(typeOf(), function.parameters[2].type) 40 | assertEquals(typeOf(), function.parameters[3].type) 41 | } 42 | 43 | @Test 44 | fun `the function has correct result type`() { 45 | val function = MessageService::class.functions.find { it.name == "sendMessage" } 46 | assertNotNull(function) { "You must define a function with name sendMessage" } 47 | assertEquals(typeOf(), function.returnType) 48 | } 49 | 50 | @Test 51 | fun `the function parameters have correct names`() { 52 | val function = MessageService::class.functions.find { it.name == "sendMessage" } 53 | assertNotNull(function) { "You must define a function with name sendMessage" } 54 | assertEquals("to", function.parameters[1].name) 55 | assertEquals("title", function.parameters[2].name) 56 | assertEquals("content", function.parameters[3].name) 57 | } 58 | 59 | @Test 60 | fun `all parameters of the function have default arguments`() { 61 | val function = MessageService::class.functions.find { it.name == "sendMessage" } 62 | assertNotNull(function) { "You must define a function with name sendMessage" } 63 | assert(function.parameters[1].isOptional) 64 | assert(function.parameters[2].isOptional) 65 | assert(function.parameters[3].isOptional) 66 | } 67 | 68 | @Test 69 | fun `calling the function with no arguments sends empty message to everyone`() { 70 | val function = messageService::class.functions.find { it.name == "sendMessage" } 71 | assertNotNull(function) { "You must define a function with name sendMessage" } 72 | function.callBy(mapOf(function.parameters[0] to messageService)) 73 | assertEquals(listOf( 74 | Email(email=EmailAddress(address="alex@gmail.com"), title="", content=""), 75 | Email(email=EmailAddress(address="jake@gmail.com"), title="", content=""), 76 | Email(email=EmailAddress(address="leon@gmail.com"), title="", content=""), 77 | Email(email=EmailAddress(address="ally@gmail.com"), title="", content="") 78 | ), sender.messagesSent) 79 | } 80 | 81 | @Test 82 | fun `calling the function with arguments sends message to concrete email`() { 83 | val function = messageService::class.functions.find { it.name == "sendMessage" } 84 | assertNotNull(function) { "You must define a function with name sendMessage" } 85 | function.callBy(mapOf( 86 | function.parameters[0] to messageService, 87 | function.parameters[1] to "jake@gmail.com", 88 | function.parameters[2] to "Some title", 89 | function.parameters[3] to "Some content", 90 | )) 91 | assertEquals(listOf( 92 | Email(email=EmailAddress(address="jake@gmail.com"), title="Some title", content="Some content"), 93 | ), sender.messagesSent) 94 | } 95 | 96 | class FakeMessageSender: MessageSender { 97 | var messagesSent = listOf() 98 | private set 99 | 100 | override fun sendEmail(email: EmailAddress, title: String, content: String) { 101 | messagesSent += Email(email, title, content) 102 | } 103 | 104 | fun clear() { 105 | messagesSent = emptyList() 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/test/java/Test.kt: -------------------------------------------------------------------------------- 1 | import javax.swing.text.View 2 | 3 | var listeners: List = listOf() 4 | fun addListener(listener: Listener) { 5 | listeners += listener 6 | } 7 | private typealias Listener = (id: Int, current: View, parent: View)->Unit 8 | 9 | fun main() { 10 | 11 | } -------------------------------------------------------------------------------- /src/test/java/Utils.kt: -------------------------------------------------------------------------------- 1 | import kotlin.test.assertEquals 2 | 3 | inline fun assertThrows(message: String? = null, body: ()->Unit): T { 4 | try { 5 | body() 6 | } catch (throwable: Throwable) { 7 | assert(throwable is T) { "The type of caught exception is not correct. It is $throwable" } 8 | if(message != null) assertEquals(message, throwable.message) 9 | return throwable as T 10 | } 11 | throw AssertionError("Body does not throw exception as expected") 12 | } -------------------------------------------------------------------------------- /src/test/java/basics/APITest.kt: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | import assertThrows 4 | import generics.Failure 5 | import generics.Response 6 | import generics.Success 7 | import org.junit.Assert.assertEquals 8 | import org.junit.Test 9 | 10 | class APITest { 11 | 12 | @Test 13 | fun `When asked for a user, correct user is returned`() { 14 | val studentsRepo = FakeStudentRepository() 15 | val analyticsRepository = FakeAnalyticsRepository() 16 | val controller = StudentController(studentsRepo, analyticsRepository) 17 | 18 | assertEquals(StudentAPI("Alec", "Strong"), controller.getStudent(1)) 19 | assertEquals(StudentAPI("Jordan", "Peterson"), controller.getStudent(2)) 20 | assertEquals(StudentAPI("Joe", "Regan"), controller.getStudent(3)) 21 | assertEquals(StudentAPI("Adam", "Savage"), controller.getStudent(4)) 22 | } 23 | 24 | @Test 25 | fun `When asked for a user that does not exist, correct error is thrown`() { 26 | val studentsRepo = FakeStudentRepository() 27 | val analyticsRepository = FakeAnalyticsRepository() 28 | val controller = StudentController(studentsRepo, analyticsRepository) 29 | 30 | val err = assertThrows("No user with id 10") { controller.getStudent(10) } 31 | assertEquals("Error code should be 400", 400, err.code) 32 | assertThrows("No user with id 0") { controller.getStudent(0) } 33 | } 34 | 35 | @Test 36 | fun `When asked for a user, ananlytics counter is bumped`() { 37 | val studentsRepo = FakeStudentRepository() 38 | val analyticsRepository = FakeAnalyticsRepository() 39 | val controller = StudentController(studentsRepo, analyticsRepository) 40 | 41 | assertEquals(0, analyticsRepository.getStudentByIdCount(1)) 42 | controller.getStudent(1) 43 | assertEquals(1, analyticsRepository.getStudentByIdCount(1)) 44 | controller.getStudent(1) 45 | controller.getStudent(1) 46 | assertEquals(3, analyticsRepository.getStudentByIdCount(1)) 47 | 48 | assertEquals(0, analyticsRepository.getStudentByIdCount(4)) 49 | controller.getStudent(4) 50 | assertEquals(1, analyticsRepository.getStudentByIdCount(4)) 51 | assertEquals(3, analyticsRepository.getStudentByIdCount(1)) 52 | } 53 | 54 | @Test 55 | fun `When asked for users, all users are returned and they are sorted by surname`() { 56 | val studentsRepo = FakeStudentRepository() 57 | val analyticsRepository = FakeAnalyticsRepository() 58 | val controller = StudentController(studentsRepo, analyticsRepository) 59 | 60 | val ret = controller.getStudents() 61 | val expected = listOf( 62 | StudentAPI("Jordan", "Peterson"), 63 | StudentAPI("Joe", "Regan"), 64 | StudentAPI("Adam", "Savage"), 65 | StudentAPI("Alec", "Strong") 66 | ) 67 | assertEquals(expected, ret) 68 | } 69 | 70 | class FakeStudentRepository: StudentRepository { 71 | private val students = listOf( 72 | StudentEntity(1, "Alec", "Strong"), 73 | StudentEntity(2, "Jordan", "Peterson"), 74 | StudentEntity(3, "Joe", "Regan"), 75 | StudentEntity(4, "Adam", "Savage") 76 | ) 77 | 78 | override fun findStudent(id: Long): StudentEntity? = 79 | students.firstOrNull { it.id == id } 80 | 81 | override fun findStudentResult(id: Long): Response { 82 | return students.firstOrNull { it.id == id } 83 | ?.let { Success(it) } 84 | ?: Failure(NotFoundException) 85 | } 86 | 87 | 88 | override fun getAllStudents(): List = 89 | students 90 | } 91 | 92 | class FakeAnalyticsRepository: AnalyticsRepository { 93 | // Maps id to count 94 | private val counter: MutableMap = mutableMapOf(1L to 0, 2L to 3, 3L to 5, 4L to 0) 95 | 96 | override fun getStudentByIdCount(id: Long): Int = counter[id] ?: throw Error("No user with such ID") 97 | 98 | override fun setStudentByIdCount(id: Long, count: Int) { 99 | counter[id] = count 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/basics/FactorialTest.kt: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | class FactorialTest { 7 | 8 | @Test 9 | fun `Test factorial results`() { 10 | val numberWithFactorial = mapOf( 11 | 0 to 1L, 12 | 1 to 1L, 13 | 2 to 2L, 14 | 3 to 6L, 15 | 4 to 24L, 16 | 10 to 3628800L, 17 | 15 to 1307674368000L, 18 | 20 to 2432902008176640000L 19 | ) 20 | for ((i, factorialResult) in numberWithFactorial) { 21 | assertEquals(factorialResult, factorial(i)) 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/test/java/basics/PersonTest.kt: -------------------------------------------------------------------------------- 1 | package basics 2 | 3 | import org.junit.Assert 4 | import org.junit.Assert.assertTrue 5 | import org.junit.Test 6 | 7 | class PersonTest { 8 | 9 | // @Test 10 | // fun businessmanImplementsPerson() { 11 | // assertTrue("Businessman needs to be a person", Businessman("AAA", 30) is Person) 12 | // } 13 | // 14 | // @Test 15 | // fun studentImplementsPerson() { 16 | // assertTrue("Student needs to be a person", Student("AAA", 30) is Person) 17 | // } 18 | // 19 | // @Test 20 | // fun personCanBuyAlcoholIfOver21() { 21 | // assertTrue("Adult businessman can buy alcohol", Businessman("AAA", 30).canBuyAlcohol) 22 | // assertTrue("Adult businessman can buy alcohol", Businessman("AAA", 21).canBuyAlcohol) 23 | // assertTrue("Young businessman can' buy alcohol", !Businessman("AAA", 19).canBuyAlcohol) 24 | // assertTrue("Young businessman can' buy alcohol", !Businessman("AAA", 17).canBuyAlcohol) 25 | // assertTrue("Adult student can buy alcohol", Student("AAA", 30).canBuyAlcohol) 26 | // assertTrue("Adult student can buy alcohol", Student("AAA", 21).canBuyAlcohol) 27 | // assertTrue("Young student can' buy alcohol", !Student("AAA", 19).canBuyAlcohol) 28 | // assertTrue("Young student can' buy alcohol", !Student("AAA", 17).canBuyAlcohol) 29 | // } 30 | // 31 | // @Test 32 | // fun testBusinessmanHelloText() { 33 | // Assert.assertEquals("Good morning", Businessman("AAA", 30).helloText()) 34 | // } 35 | // 36 | // @Test 37 | // fun testStudentHelloText() { 38 | // Assert.assertEquals("Hi", Student("AAA", 30).helloText()) 39 | // } 40 | // 41 | // @Test 42 | // fun testStudentGreetText() { 43 | // val name1 = "Some name" 44 | // val name2 = "Another name" 45 | // val student = Student(name1, 12) 46 | // val businessman = Businessman(name2, 12) 47 | // Assert.assertEquals("Hey $name2, I am $name1", Student(name1, 30).cheerText(businessman)) 48 | // Assert.assertEquals("Hey $name1, I am $name2", Student(name2, 30).cheerText(student)) 49 | // } 50 | // 51 | // @Test 52 | // fun testBusinessmanGreetText() { 53 | // val name1 = "Some name" 54 | // val name2 = "Another name" 55 | // val student = Student(name1, 12) 56 | // val businessman = Businessman(name2, 12) 57 | // Assert.assertEquals("Hello, my name is $name1, nice to see you $name2", Businessman(name1, 30).cheerText(businessman)) 58 | // Assert.assertEquals("Hello, my name is $name2, nice to see you $name1", Businessman(name2, 30).cheerText(student)) 59 | // } 60 | } -------------------------------------------------------------------------------- /src/test/java/basics/TreeOperationsTest.kt: -------------------------------------------------------------------------------- 1 | package basics.treeoperations 2 | 3 | import junit.framework.TestCase.assertEquals 4 | import junit.framework.TestCase.assertTrue 5 | import org.junit.Test 6 | 7 | class TreeOperationsTest { 8 | /* 9 | root Node 10 | / \ 11 | Node CCC 12 | / \ 13 | AAA BBB 14 | */ 15 | private val tree1 = Node(Node(Leaf("AAA"), Leaf("BBB")), Leaf("CCC")) 16 | 17 | /* 18 | root Node 19 | / \ 20 | Node Node 21 | / \ / \ 22 | Node CCC Node CCC 23 | / \ / \ 24 | AAA BBB AAA BBB 25 | */ 26 | private val tree2 = 27 | Node(Node(Node(Leaf("AAA"), Leaf("BBB")), Leaf("CCC")), Node(Node(Leaf("AAA"), Leaf("BBB")), Leaf("CCC"))) 28 | 29 | /* 30 | root Node 31 | / \ 32 | Node DDD 33 | / \ 34 | Node CCC 35 | / \ 36 | AAA BBB 37 | */ 38 | private val tree3 = Node(Node(Node(Leaf("AAA"), Leaf("BBB")), Leaf("CCC")), Leaf("DDD")) 39 | 40 | /* 41 | root Node 42 | / \ 43 | Node 3 44 | / \ 45 | Node 4 46 | / \ 47 | 5 3 48 | */ 49 | val treeInt = Node(Node(Node(Leaf(5), Leaf(3)), Leaf(4)), Leaf(3)) 50 | 51 | // @Test 52 | // fun `Count of a leaf is 1`() { 53 | // assertEquals(1, Leaf("AAA").count()) 54 | // } 55 | // 56 | // @Test 57 | // fun `Count of a single node with two leafs is 2`() { 58 | // assertEquals(2, Node(Leaf("AAA"), Leaf("BBB")).count()) 59 | // } 60 | // 61 | // @Test 62 | // fun `Count returns number of leafs in the tree`() { 63 | // assertEquals(3, tree1.count()) 64 | // assertEquals(6, tree2.count()) 65 | // assertEquals(4, tree3.count()) 66 | // } 67 | // 68 | // @Test 69 | // fun `countAll of a leaf is 1`() { 70 | // assertEquals(1, Leaf("AAA").countAll()) 71 | // } 72 | // 73 | // @Test 74 | // fun `countAll of a single node with two leafs is 3`() { 75 | // assertEquals(3, Node(Leaf("AAA"), Leaf("BBB")).countAll()) 76 | // } 77 | // 78 | // @Test 79 | // fun `countAll returns number of leafs in the tree`() { 80 | // assertEquals(5, tree1.countAll()) 81 | // } 82 | // 83 | // @Test 84 | // fun `Height of a leaf is 1`() { 85 | // assertEquals(1, Leaf("AAA").height()) 86 | // } 87 | // 88 | // @Test 89 | // fun `Height of a single node with two leafs is 2`() { 90 | // assertEquals(2, Node(Leaf("AAA"), Leaf("BBB")).height()) 91 | // } 92 | // 93 | // @Test 94 | // // Every level in height is one more to the result 95 | // fun `Height returns the highest distance from the root to a leaf`() { 96 | // assertEquals(3, tree1.height()) 97 | // assertEquals(4, tree2.height()) 98 | // assertEquals(4, tree3.height()) 99 | // } 100 | // 101 | // @Test 102 | // fun `Biggest on leaf returns its element`() { 103 | // assertEquals(1, Leaf(1).biggest()) 104 | // assertEquals("Z", Leaf("Z").biggest()) 105 | // assertEquals(Float.POSITIVE_INFINITY, Leaf(Float.POSITIVE_INFINITY).biggest()) 106 | // } 107 | // 108 | // @Test 109 | // fun biggestTest() { 110 | // assertEquals("CCC", tree1.biggest()) 111 | // assertEquals("CCC", tree2.biggest()) 112 | // assertEquals("DDD", tree3.biggest()) 113 | // assertEquals(5, treeInt.biggest()) 114 | // } 115 | // 116 | // @Test 117 | // fun sumTest() { 118 | // assertEquals(15, treeInt.sum()) 119 | // } 120 | // 121 | // @Test 122 | // fun `Value of a leaf is in this leaf`() { 123 | // assertTrue(Leaf("AAA").contains("AAA")) 124 | // } 125 | // 126 | // @Test 127 | // fun `Node with leafs contains values of its leafs`() { 128 | // val tree = Node(Leaf("AAA"), Leaf("BBB")) 129 | // assertTrue(tree.contains("AAA")) 130 | // assertTrue(tree.contains("BBB")) 131 | // assertTrue(!tree.contains("CCC")) 132 | // assertTrue(!tree.contains("DDD")) 133 | // } 134 | // 135 | // @Test 136 | // fun `Complex cases`() { 137 | // assertTrue("AAA" in tree1) 138 | // assertTrue("BBB" in tree1) 139 | // assertTrue("CCC" in tree1) 140 | // assertTrue("CCCC" !in tree1) 141 | // assertTrue("D" !in tree1) 142 | // 143 | // assertTrue("AAA" in tree2) 144 | // assertTrue("BBB" in tree2) 145 | // assertTrue("CCC" in tree2) 146 | // assertTrue("CCCC" !in tree2) 147 | // assertTrue("D" !in tree2) 148 | // 149 | // 150 | // assertTrue("AAA" in tree3) 151 | // assertTrue("BBB" in tree3) 152 | // assertTrue("CCC" in tree3) 153 | // assertTrue("DDD" in tree3) 154 | // assertTrue("CCCC" !in tree3) 155 | // assertTrue("D" !in tree3) 156 | // } 157 | // 158 | // @Test 159 | // fun `Plus of two leafs test`() { 160 | // val tree = Leaf("AAA") + Leaf("BBB") 161 | // assertTrue(tree is Node) 162 | // val n = tree as Node 163 | // assertTrue(n.left == Leaf("AAA")) 164 | // assertTrue(n.right == Leaf("BBB")) 165 | // } 166 | // 167 | // @Test 168 | // fun `Plus just places subtrees on the left and right side without copying them`() { 169 | // val tree = tree1 + tree2 170 | // assertTrue(tree is Node) 171 | // val n = tree as Node 172 | // assertTrue(n.left === tree1) 173 | // assertTrue(n.right === tree2) 174 | // } 175 | } 176 | -------------------------------------------------------------------------------- /src/test/java/collections/BestStudentsListTest.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import kotlin.test.assertEquals 4 | import org.junit.Test 5 | 6 | @Suppress("FunctionName") 7 | class BestStudentsListTest { 8 | 9 | @Test 10 | fun `Single student that matches criteria gets biggest internship`() { 11 | val text = listOf(internshipStudent).makeBestStudentsList() 12 | val expected = "Marc Smith, \$5000" 13 | assertEquals(expected, text) 14 | } 15 | 16 | @Test 17 | fun `Single student with too low result doesn't get internship`() { 18 | val text = listOf(studentWithTooLowResultToInternship).makeBestStudentsList() 19 | assertEquals("", text) 20 | } 21 | 22 | @Test 23 | fun `Result 80 is acceptable`() { 24 | val student = Student("Noely", "Peterson", 80.0, 32) 25 | val text = listOf(student).makeBestStudentsList() 26 | assertEquals("Noely Peterson, \$5000", text) 27 | } 28 | 29 | @Test 30 | fun `30 points is acceptable`() { 31 | val student = Student("Noely", "Peterson", 81.0, 30) 32 | val text = listOf(student).makeBestStudentsList() 33 | assertEquals("Noely Peterson, \$5000", text) 34 | } 35 | 36 | @Test 37 | fun `Single student with not enough doesn't get internship`() { 38 | val text = listOf(studentWithNotEnoughPointsForInternship).makeBestStudentsList() 39 | assertEquals("", text) 40 | } 41 | 42 | @Test 43 | fun `Complex test`() { 44 | val text = allStudents.makeBestStudentsList() 45 | val expected = """ 46 | Ester Adams, ${'$'}1000 47 | Dior Angel, ${'$'}3000 48 | Oregon Dart, ${'$'}1000 49 | Jack Johnson, ${'$'}1000 50 | James Johnson, ${'$'}1000 51 | Jon Johnson, ${'$'}1000 52 | Naja Marcson, ${'$'}5000 53 | Alex Nolan, ${'$'}1000 54 | Ron Peters, ${'$'}3000 55 | Marc Smith, ${'$'}3000 56 | """.trimIndent() 57 | assertEquals(expected, text) 58 | } 59 | } -------------------------------------------------------------------------------- /src/test/java/collections/MapTest.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import org.junit.* 4 | import org.junit.Assert.* 5 | 6 | // TODO: Uncomment it 7 | //import kotlin.collections.map as stdlibMap 8 | 9 | class MapTest { 10 | 11 | @Test 12 | fun mapTests() { 13 | val numbers = 1..5 14 | val names = listOf("Mike", "Jane", "Marcin", "John", "James") 15 | 16 | val upper = names.map { it.uppercase() } 17 | val doubled = numbers.map { it * 2 } 18 | 19 | assertEquals(listOf("MIKE", "JANE", "MARCIN", "JOHN", "JAMES"), upper) 20 | assertEquals(listOf(2, 4, 6, 8, 10), doubled) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/collections/PassingStudentsListTest.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | @Suppress("FunctionName") 7 | class PassingStudentsListTest { 8 | 9 | @Test 10 | fun `Single student that matches criteria is displayed`() { 11 | val text = listOf(internshipStudent).makePassingStudentsListText() 12 | val expected = "Marc Smith, 87.0" 13 | assertEquals(expected, text) 14 | } 15 | 16 | @Test 17 | fun `Single student with too low result doesn't get internship`() { 18 | val text = listOf(studentNotPassingBecauseOfResult).makePassingStudentsListText() 19 | assertEquals("", text) 20 | } 21 | 22 | @Test 23 | fun `15 points is not acceptable`() { 24 | val student = Student("Noely", "Peterson", 81.0, 15) 25 | val text = listOf(student).makePassingStudentsListText() 26 | assertEquals("", text) 27 | } 28 | 29 | @Test 30 | fun `result 50 points is acceptable`() { 31 | val student = Student("Noely", "Peterson", 50.0, 25) 32 | val text = listOf(student).makePassingStudentsListText() 33 | assertEquals("Noely Peterson, 50.0", text) 34 | } 35 | 36 | @Test 37 | fun `Students are displayed in an alphanumerical order sorted by surname and then by name`() { 38 | val students = listOf( 39 | Student(name = "B", surname = "A", result = 81.0, pointsInSemester = 16), 40 | Student(name = "B", surname = "B", result = 82.0, pointsInSemester = 16), 41 | Student(name = "A", surname = "A", result = 83.0, pointsInSemester = 16), 42 | Student(name = "A", surname = "B", result = 84.0, pointsInSemester = 16) 43 | ) 44 | 45 | // When 46 | val text = students.makePassingStudentsListText() 47 | 48 | // Then 49 | val expected = """ 50 | A A, 83.0 51 | B A, 81.0 52 | A B, 84.0 53 | B B, 82.0 54 | """.trimIndent() 55 | assertEquals(expected, text) 56 | } 57 | 58 | @Test 59 | fun `Single student with not enough doesn't get internship`() { 60 | val text = listOf(studentNotPassingBecauseOfPoints).makePassingStudentsListText() 61 | assertEquals("", text) 62 | } 63 | 64 | @Test 65 | fun `Complex test`() { 66 | val text = allStudents.makePassingStudentsListText() 67 | val expected = """ 68 | Ester Adams, 81.0 69 | Dior Angel, 88.5 70 | Oregon Dart, 85.5 71 | Jack Johnson, 85.3 72 | James Johnson, 85.2 73 | Jon Johnson, 85.1 74 | Jamme Lannister, 80.0 75 | Naja Marcson, 100.0 76 | Alex Nolan, 86.0 77 | Ron Peters, 89.0 78 | Noe Peterson, 91.0 79 | Noely Peterson, 91.0 80 | Harry Potter, 80.0 81 | Marc Smith, 87.0 82 | """.trimIndent() 83 | assertEquals(expected, text) 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /src/test/java/collections/PlusAtTest.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import org.junit.Assert.assertTrue 4 | import org.junit.Test 5 | import kotlin.test.assertEquals 6 | 7 | @Suppress("FunctionName") 8 | class PlusAtTest { 9 | 10 | @Test 11 | fun `Simple addition to the middle adds correctly at the position`() { 12 | assertEquals(listOf(1, 2, 7, 3), listOf(1, 2, 3).plusAt(2, 7)) 13 | assertEquals(listOf("1", "2", "7", "3"), listOf("1", "2", "3").plusAt(2, "7")) 14 | } 15 | 16 | @Test 17 | fun `When we add at size position, element is added at the end`() { 18 | assertEquals(listOf(1, 2, 3, 7), listOf(1, 2, 3).plusAt(3, 7)) 19 | assertEquals(listOf("1", "2", "3", "7"), listOf("1", "2", "3").plusAt(3, "7")) 20 | } 21 | 22 | @Test 23 | fun `When we add at 0, element is added at the beginning`() { 24 | assertEquals(listOf(7, 1, 2, 3), listOf(1, 2, 3).plusAt(0, 7)) 25 | assertEquals(listOf("7", "1", "2", "3"), listOf("1", "2", "3").plusAt(0, "7")) 26 | } 27 | 28 | @Test 29 | fun `When we try to insert at illegal position, IllegalArgumentException error is thrown`() { 30 | assertTrue(catchError { listOf(1, 2, 3).plusAt(-1, 7) } is IllegalArgumentException) 31 | assertTrue(catchError { listOf(1, 2, 3).plusAt(8, 7) } is IllegalArgumentException) 32 | assertTrue(catchError { listOf(1, 2, 3).plusAt(10, 7) } is IllegalArgumentException) 33 | assertTrue(catchError { listOf(1, 2, 3).plusAt(100, 7) } is IllegalArgumentException) 34 | 35 | } 36 | 37 | private fun catchError(f: () -> Unit): Throwable? = try { 38 | f() 39 | null 40 | } catch (e: Throwable) { 41 | e 42 | } 43 | } -------------------------------------------------------------------------------- /src/test/java/collections/QuickSortTest.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import org.junit.Test 4 | import java.util.* 5 | import kotlin.test.assertEquals 6 | 7 | @Suppress("FunctionName") 8 | class QuickSortTest { 9 | 10 | @Test 11 | fun `Empty list is sorted`() { 12 | assertEquals(emptyList(), emptyList().quickSort()) 13 | } 14 | 15 | @Test 16 | fun `Single element is sorted`() { 17 | assertEquals(listOf(1), listOf(1).quickSort()) 18 | } 19 | 20 | @Test 21 | fun `Simple numbers sorting`() { 22 | assertEquals(listOf(1, 2, 3, 5, 6), listOf(3, 2, 5, 1, 6).quickSort()) 23 | } 24 | 25 | @Test 26 | fun `Simple String sorting`() { 27 | assertEquals(listOf("A", "B", "C", "D"), listOf("C", "D", "A", "B").quickSort()) 28 | } 29 | 30 | @Test 31 | fun `Random list sorting should give the same result as when we use stdlib sorted function`() { 32 | val rand = Random(244252) 33 | val listOfRandomLists = (1..100).map { _ -> (1..100).map { rand.nextInt() } } 34 | for (list: List in listOfRandomLists) { 35 | assertEquals(list.sorted(), list.quickSort()) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/test/java/collections/StudentsData.kt: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | val internshipStudent = Student("Marc", "Smith", 87.0, 32) 4 | val studentWithTooLowResultToInternship = Student("Marcus", "Smith", 37.0, 32) 5 | val studentWithNotEnoughPointsForInternship = Student("Marcello", "Smith", 87.0, 12) 6 | val studentNotPassingBecauseOfResult = Student("Peter", "Jackson", 21.0, 24) 7 | val studentNotPassingBecauseOfPoints = Student("Michael", "Angelo", 71.0, 12) 8 | 9 | val allStudents = listOf( 10 | internshipStudent, 11 | studentWithTooLowResultToInternship, 12 | studentWithNotEnoughPointsForInternship, 13 | studentNotPassingBecauseOfResult, 14 | Student("Noely", "Peterson", 91.0, 22), 15 | studentNotPassingBecauseOfPoints, 16 | Student("Noe", "Samson", 41.0, 18), 17 | Student("Timothy", "Johnson", 51.0, 15), 18 | Student("Noe", "Peterson", 91.0, 22), 19 | Student("Ester", "Adams", 81.0, 30), 20 | Student("Dior", "Angel", 88.5, 38), 21 | Student("Naja", "Marcson", 100.0, 31), 22 | Student("Oregon", "Dart", 85.5, 30), 23 | Student("Ron", "Peters", 89.0, 31), 24 | Student("Harry", "Potter", 80.0, 30), 25 | Student("Sansa", "Stark", 49.5, 14), 26 | Student("Jamme", "Lannister", 80.0, 30), 27 | Student("Alex", "Nolan", 86.0, 33), 28 | Student("Jon", "Johnson", 85.1, 31), 29 | Student("James", "Johnson", 85.2, 31), 30 | Student("Jack", "Johnson", 85.3, 31) 31 | ) -------------------------------------------------------------------------------- /src/test/java/corotuines/CoroutineTest.kt: -------------------------------------------------------------------------------- 1 | package corotuines 2 | 3 | import kotlinx.coroutines.runBlocking 4 | import org.junit.Test 5 | 6 | class CoroutineTest { 7 | @Test 8 | fun testMySuspendingFunction() = runBlocking { 9 | //... 10 | } 11 | } -------------------------------------------------------------------------------- /src/test/java/corotuines/FibonacciTest.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | @Suppress("FunctionName") 7 | internal class FibonacciTest { 8 | 9 | @Test 10 | fun `First two numbers should be 1 and 1`() { 11 | assertEquals(listOf(1, 1), fibonacci.take(2).toList()) 12 | } 13 | 14 | @Test 15 | fun `Check first 11 numbers`() { 16 | assertEquals(listOf(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89), fibonacci.take(11).toList()) 17 | } 18 | } -------------------------------------------------------------------------------- /src/test/java/corotuines/FlowKataTests.kt: -------------------------------------------------------------------------------- 1 | package corotuines 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.flow.flow 5 | import kotlinx.coroutines.flow.* 6 | import kotlinx.coroutines.flow.toList 7 | import kotlinx.coroutines.launch 8 | import kotlinx.coroutines.sync.Mutex 9 | import kotlinx.coroutines.sync.withLock 10 | import kotlinx.coroutines.test.runTest 11 | import org.junit.Test 12 | import java.util.concurrent.atomic.AtomicInteger 13 | import kotlin.test.assertEquals 14 | 15 | @Suppress("FunctionName") 16 | class FlowKataTests { 17 | 18 | @Test() 19 | fun producingUnitsTests() = runTest { 20 | assertEquals(listOf(), producingUnits(0).toList()) 21 | assertEquals(listOf(Unit), producingUnits(1).toList()) 22 | assertEquals(listOf(Unit, Unit), producingUnits(2).toList()) 23 | assertEquals(listOf(Unit, Unit, Unit), producingUnits(3).toList()) 24 | for (i in 1..100 step 7) { 25 | assertEquals(List(i) { Unit }, producingUnits(i).toList()) 26 | } 27 | } 28 | 29 | @Test() 30 | fun toToggleTests() = runTest { 31 | assertEquals(listOf(), producingUnits(0).toToggle().toList()) 32 | assertEquals(listOf(true), producingUnits(1).toToggle().toList()) 33 | assertEquals(listOf(true, false), producingUnits(2).toToggle().toList()) 34 | assertEquals(listOf(true, false, true), producingUnits(3).toToggle().toList()) 35 | assertEquals(listOf(true, false, true, false), producingUnits(4).toToggle().toList()) 36 | } 37 | 38 | @Test() 39 | fun toNextNumbersTests() = runTest { 40 | assertEquals(listOf(), producingUnits(0).toNextNumbers().toList()) 41 | assertEquals(listOf(1), producingUnits(1).toNextNumbers().toList()) 42 | assertEquals(listOf(1, 2), producingUnits(2).toNextNumbers().toList()) 43 | assertEquals(listOf(1, 2, 3), producingUnits(3).toNextNumbers().toList()) 44 | for (i in 1..100 step 7) { 45 | val list = List(i) { it + 1 } 46 | assertEquals(list, list.map { Unit }.asFlow().toNextNumbers().toList()) 47 | } 48 | } 49 | 50 | @Test() 51 | fun withHistoryTests() = runTest { 52 | assertEquals(listOf(listOf()), producingUnits(0).withHistory().toList()) 53 | assertEquals(listOf(listOf(), listOf(Unit)), producingUnits(1).withHistory().toList()) 54 | assertEquals(listOf(listOf(), listOf(Unit), listOf(Unit, Unit)), producingUnits(2).withHistory().toList()) 55 | 56 | assertEquals( 57 | listOf(listOf(), listOf(1), listOf(1, 2)), 58 | producingUnits(2).toNextNumbers().withHistory().toList() 59 | ) 60 | assertEquals( 61 | listOf(listOf(), listOf(true), listOf(true, false)), 62 | producingUnits(2).toToggle().withHistory().toList() 63 | ) 64 | 65 | val flow = flow { 66 | emit("A") 67 | delay(100) 68 | emit(10) 69 | emit("C") 70 | } 71 | assertEquals(listOf(listOf(), listOf("A"), listOf("A", 10), listOf("A", 10, "C")), flow.withHistory().toList()) 72 | } 73 | 74 | @Test() 75 | fun flowDelayEachTests() = runTest { 76 | val emittedNum = AtomicInteger() 77 | 78 | producingUnits(100) 79 | .delayEach(1000) 80 | .onEach { emittedNum.incrementAndGet() } 81 | .launchIn(this) 82 | 83 | assertEquals(0, emittedNum.get()) 84 | 85 | // After 1 500ms there should be one element 86 | delay(1_500) 87 | assertEquals(1, emittedNum.get()) 88 | 89 | // After another 2 000ms there should be two more elements 90 | delay(2_000) 91 | assertEquals(3, emittedNum.get()) 92 | 93 | // After another 12 000ms there should be twelve more elements 94 | delay(12_000) 95 | assertEquals(15, emittedNum.get()) 96 | } 97 | 98 | @Test() 99 | fun makeTimerTests() = runTest { 100 | val mutex = Mutex() 101 | var ticked = listOf() 102 | makeTimer(1000, 10, 20) 103 | .onEach { 104 | mutex.withLock { ticked += it } 105 | } 106 | .launchIn(this) 107 | 108 | assertEquals(listOf(10), mutex.withLock { ticked }) 109 | 110 | // After 1 500ms there should be one element 111 | delay(1_500) 112 | assertEquals(listOf(10, 11), mutex.withLock { ticked }) 113 | 114 | // After another 2 000ms there should be two more elements 115 | delay(2_000) 116 | assertEquals(listOf(10, 11, 12, 13), mutex.withLock { ticked }) 117 | 118 | // After another 12 000ms there should be twelve more elements 119 | delay(12_000) 120 | assertEquals((10..20).toList(), mutex.withLock { ticked }) 121 | } 122 | 123 | @Test() 124 | fun `makeTimer if delayed in between, do not provide old values but only shows the last one`() = runTest { 125 | val maxValue = 20 126 | val res = makeTimer(100, 1, maxValue) 127 | .onEach { 128 | if (it == 1) delay(50) // To make it clearly after timer delay 129 | // We don't need to check more often than every 0.5s 130 | delay(500) 131 | } 132 | .toList() 133 | 134 | assertEquals(listOf(1, 6, 11, 16, 20), res) 135 | } 136 | 137 | @Test() 138 | fun makeLightSwitchTests() = runTest { 139 | val switchOne = flow { 140 | emit(true) 141 | delay(1000) 142 | emit(false) 143 | delay(10) 144 | emit(true) 145 | delay(500) // 1500 146 | emit(false) 147 | } 148 | val switchTwo = flow { 149 | emit(false) 150 | delay(200) 151 | emit(true) 152 | delay(1000) // 1200 153 | emit(false) 154 | } 155 | 156 | var lightOn = false 157 | launch { 158 | makeLightSwitch(switchOne, switchTwo).collect { 159 | lightOn = it 160 | } 161 | } 162 | 163 | delay(50) 164 | assertEquals(true, lightOn) 165 | delay(200) // 250 166 | assertEquals(false, lightOn) 167 | delay(800) // 1050 168 | assertEquals(false, lightOn) 169 | delay(200) // 1250 170 | assertEquals(true, lightOn) 171 | delay(300) // 1550 172 | assertEquals(false, lightOn) 173 | } 174 | 175 | @Test() 176 | fun makeLightSwitchToggleTests() = runTest { 177 | val switchOne = flow { 178 | emit(Unit) 179 | delay(1000) 180 | emit(Unit) 181 | delay(10) 182 | emit(Unit) 183 | delay(500) // 1500 184 | emit(Unit) 185 | } 186 | val switchTwo = flow { 187 | emit(Unit) 188 | delay(200) 189 | emit(Unit) 190 | delay(1000) // 1200 191 | emit(Unit) 192 | } 193 | 194 | var lightOn = false 195 | launch { 196 | makeLightSwitchToggle(switchOne, switchTwo).collect { 197 | lightOn = it 198 | } 199 | } 200 | 201 | delay(50) 202 | assertEquals(true, lightOn) 203 | delay(200) // 250 204 | assertEquals(false, lightOn) 205 | delay(800) // 1050 206 | assertEquals(false, lightOn) 207 | delay(200) // 1250 208 | assertEquals(true, lightOn) 209 | delay(300) // 1550 210 | assertEquals(false, lightOn) 211 | } 212 | 213 | @Test() 214 | fun polonaisePairingTests() = runTest { 215 | val track1 = flow { 216 | emit(Person("A")) 217 | emit(Person("B")) 218 | delay(1000) 219 | emit(Person("C")) 220 | emit(Person("D")) 221 | } 222 | val track2 = flow { 223 | emit(Person("1")) 224 | delay(600) 225 | emit(Person("2")) 226 | delay(1000) 227 | emit(Person("3")) 228 | } 229 | 230 | val res = polonaisePairing(track1, track2).toList() 231 | val expected = listOf("A" to "1", "B" to "2", "C" to "3").map { Person(it.first) to Person(it.second) } 232 | assertEquals(expected, res) 233 | 234 | var lastPair: Pair? = null 235 | launch { 236 | polonaisePairing(track1, track2).collect { lastPair = it } 237 | } 238 | 239 | assertEquals(Person("A") to Person("1"), lastPair) 240 | delay(200) // 200 241 | assertEquals(Person("A") to Person("1"), lastPair) 242 | 243 | delay(500) // 700 244 | assertEquals(Person("B") to Person("2"), lastPair) 245 | delay(500) // 1200 246 | assertEquals(Person("B") to Person("2"), lastPair) 247 | 248 | delay(500) // 1700 249 | assertEquals(Person("C") to Person("3"), lastPair) 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/test/java/corotuines/Main.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.runBlocking 5 | 6 | fun main(): Unit = runBlocking { 7 | println("Started!") 8 | test() 9 | println("Done.") 10 | } 11 | 12 | suspend fun test() { 13 | delay(1000) 14 | } -------------------------------------------------------------------------------- /src/test/java/corotuines/SieveTest.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | @Suppress("FunctionName") 7 | class SieveTest { 8 | 9 | @Test 10 | fun `First prime number is 2`() { 11 | assert(2 in primes) 12 | assertEquals(0, primes.indexOf(2)) 13 | assertEquals(2, primes.first()) 14 | } 15 | 16 | @Test 17 | fun `Second prime number is 3`() { 18 | assert(3 in primes) 19 | assertEquals(1, primes.indexOf(3)) 20 | assertEquals(3, primes.take(2).last()) 21 | } 22 | 23 | @Test 24 | fun `Third prime number is 5`() { 25 | assert(5 in primes) 26 | assertEquals(2, primes.indexOf(5)) 27 | assertEquals(5, primes.take(3).last()) 28 | } 29 | 30 | @Test 31 | fun `Check first 10 prime numbers`() { 32 | assertEquals(listOf(2, 3, 5, 7, 11, 13, 17, 19, 23, 29), primes.take(10).toList()) 33 | } 34 | 35 | @Test 36 | fun `Check first 100 prime numbers`() { 37 | val first100primes = listOf( 38 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 39 | 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 40 | 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 41 | 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 42 | 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541 43 | ) 44 | assertEquals(first100primes, primes.take(100).toList()) 45 | } 46 | 47 | @Test 48 | fun `Prime at 200th position is 1223`() { 49 | assertEquals(1223, primes.take(200).last()) 50 | } 51 | } -------------------------------------------------------------------------------- /src/test/java/corotuines/continuation/ContinuationStealTest.kt: -------------------------------------------------------------------------------- 1 | package continuation 2 | 3 | import org.junit.Test 4 | import kotlin.coroutines.resume 5 | import kotlin.test.assertEquals 6 | import kotlin.test.assertNotNull 7 | 8 | @Suppress("FunctionName") 9 | class ContinuationStealTest { 10 | 11 | private val fakeText = "This is some text" 12 | 13 | class FakeConsole: Console() { 14 | val printed = mutableListOf() 15 | 16 | override fun println(text: Any?) { 17 | printed += text 18 | } 19 | } 20 | 21 | @Test(timeout = 500) 22 | fun `At the beginning function says "Before"`() { 23 | val fakeConsole = FakeConsole() 24 | continuationSteal(fakeConsole) 25 | assertEquals("Before", fakeConsole.printed.first()) 26 | } 27 | 28 | @Test(timeout = 500) 29 | fun `At the end function says "After"`() { 30 | val fakeConsole = FakeConsole() 31 | val cont = continuationSteal(fakeConsole) 32 | cont?.resume(fakeText) 33 | assertEquals("After", fakeConsole.printed.last()) 34 | } 35 | 36 | @Test(timeout = 500) 37 | fun `In the middle, we suspend function`() { 38 | val fakeConsole = FakeConsole() 39 | val cont = continuationSteal(fakeConsole) 40 | assertEquals(mutableListOf("Before"), fakeConsole.printed) 41 | } 42 | 43 | @Test(timeout = 500) 44 | fun `Function should return continuation`() { 45 | val fakeConsole = FakeConsole() 46 | val cont = continuationSteal(fakeConsole) 47 | assertNotNull(cont) 48 | cont.resume(fakeText) 49 | assertEquals("After", fakeConsole.printed.last()) 50 | } 51 | 52 | @Test(timeout = 500) 53 | fun `Only "Before" is printed before resume`() { 54 | val fakeConsole = FakeConsole() 55 | val cont = continuationSteal(fakeConsole) 56 | assertNotNull(cont) 57 | assertEquals("Before", fakeConsole.printed.first()) 58 | } 59 | 60 | @Test(timeout = 500) 61 | fun `After resume function should print text to resume`() { 62 | val fakeConsole = FakeConsole() 63 | val cont = continuationSteal(fakeConsole) 64 | cont?.resume(fakeText) 65 | assertEquals(3, fakeConsole.printed.size) 66 | assertEquals(fakeText, fakeConsole.printed[1]) 67 | } 68 | } -------------------------------------------------------------------------------- /src/test/java/corotuines/request/RequestTest.kt: -------------------------------------------------------------------------------- 1 | package corotuines.request 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.runBlocking 6 | import kotlinx.coroutines.sync.Mutex 7 | import kotlinx.coroutines.sync.withLock 8 | import org.junit.Test 9 | import kotlin.system.measureTimeMillis 10 | 11 | class RequestTest { 12 | 13 | @Test 14 | fun `Function does return the best student in the semester`() = runBlocking { 15 | val semester = "19L" 16 | val best = Student(2, 95.0, semester) 17 | val repo = ImmediateFakeStudentRepo(listOf( 18 | Student(1, 90.0, semester), 19 | best, 20 | Student(3, 50.0, semester) 21 | )) 22 | val chosen = getBestStudent(semester, repo) 23 | kotlin.test.assertEquals(best, chosen) 24 | } 25 | 26 | @Test 27 | fun `When no students, correct error is thrown`() = runBlocking { 28 | val semester = "19L" 29 | val best = Student(2, 95.0, semester) 30 | val repo = ImmediateFakeStudentRepo(listOf()) 31 | assertThrowsError { 32 | val chosen = getBestStudent(semester, repo) 33 | } 34 | } 35 | 36 | @Test 37 | fun `Requests do not wait for each other`() = runBlocking { 38 | val repo = WaitingFakeStudentRepo() 39 | assertTimeAround(1200) { 40 | val chosen = getBestStudent("AAA", repo) 41 | } 42 | } 43 | 44 | @Test 45 | fun `Cancellation works fine`() = runBlocking { 46 | val repo = WaitingFakeStudentRepo() 47 | val job = launch { 48 | val chosen = getBestStudent("AAA", repo) 49 | } 50 | delay(300) 51 | job.cancel() 52 | delay(1000) 53 | kotlin.test.assertEquals(0, repo.returnedStudents) 54 | } 55 | 56 | @Test 57 | fun `When one request has error, all are stopped and error is thrown`() = runBlocking { 58 | val repo = FirstFailingFakeStudentRepo() 59 | assertThrowsError { 60 | getBestStudent("AAA", repo) 61 | } 62 | delay(1000) 63 | kotlin.test.assertEquals(0, repo.studentsReturned, "Looks like some requests were still running after the first one had an error") 64 | } 65 | } 66 | 67 | class ImmediateFakeStudentRepo( 68 | private val students: List 69 | ) : StudentsRepository { 70 | 71 | override suspend fun getStudentIds(semester: String): List = 72 | students.filter { it.semester == semester } 73 | .map { it.id } 74 | 75 | override suspend fun getStudent(id: Int): Student = 76 | students.first { it.id == id } 77 | } 78 | 79 | inline fun assertTimeAround(expectedTime: Int, upperMargin: Int = 100, body: () -> Unit) { 80 | val actualTime = measureTimeMillis(body) 81 | assert(actualTime in expectedTime..(expectedTime + upperMargin)) { 82 | "Operation should take around $expectedTime, but it took $actualTime" 83 | } 84 | } 85 | 86 | inline fun assertThrowsError(body: ()->Unit) { 87 | try { 88 | body() 89 | assert(false) { "There should be an error of type ${T::class.simpleName}" } 90 | } catch (throwable: Throwable) { 91 | if(throwable !is T) { 92 | throw throwable 93 | } 94 | } 95 | } 96 | 97 | class WaitingFakeStudentRepo : StudentsRepository { 98 | var returnedStudents = 0 99 | 100 | override suspend fun getStudentIds(semester: String): List { 101 | delay(200) 102 | return (1..5).toList() 103 | } 104 | 105 | override suspend fun getStudent(id: Int): Student { 106 | delay(1000) 107 | returnedStudents++ 108 | return Student(12, 12.0, "AAA") 109 | } 110 | } 111 | 112 | class FirstFailingFakeStudentRepo : StudentsRepository { 113 | var first = true 114 | var studentsReturned = 0 115 | val mutex = Mutex() 116 | 117 | override suspend fun getStudentIds(semester: String): List { 118 | delay(200) 119 | return (1..5).toList() 120 | } 121 | 122 | override suspend fun getStudent(id: Int): Student { 123 | delay(100) 124 | mutex.withLock { // To prevent more than one throwing 125 | if (first) { 126 | first = false 127 | throw FirstFailingError() 128 | } 129 | } 130 | delay(100) 131 | studentsReturned++ 132 | return Student(12, 12.0, "AAA") 133 | } 134 | 135 | class FirstFailingError(): Error() 136 | } -------------------------------------------------------------------------------- /src/test/java/corotuines/ui/BasePresenterTest.kt: -------------------------------------------------------------------------------- 1 | package coroutines.ui 2 | 3 | import kotlinx.coroutines.* 4 | import org.junit.Test 5 | import kotlin.coroutines.CoroutineContext 6 | import kotlin.coroutines.EmptyCoroutineContext 7 | import kotlin.test.assertEquals 8 | 9 | @Suppress("FunctionName") 10 | class BasePresenterTest { 11 | 12 | class FakePresenter( 13 | private val jobInterceptor: (() -> Unit)? = null, 14 | onError: (Throwable) -> Unit = {} 15 | ) : BasePresenter(onError) { 16 | 17 | var cancelledJobs = 0 18 | 19 | fun onCreate() { 20 | // launch { 21 | // try { 22 | // delay(100) 23 | // jobInterceptor?.invoke() 24 | // delay(2000) 25 | // } finally { 26 | // cancelledJobs += 1 27 | // } 28 | // } 29 | // launch { 30 | // try { 31 | // delay(100) 32 | // jobInterceptor?.invoke() 33 | // delay(2000) 34 | // } finally { 35 | // cancelledJobs += 1 36 | // } 37 | // } 38 | } 39 | } 40 | 41 | @Test 42 | fun `onDestroy cancels all jobs`() = runBlocking { 43 | val presenter = FakePresenter() 44 | presenter.onCreate() 45 | delay(200) 46 | presenter.onDestroy() 47 | delay(200) 48 | assertEquals(2, presenter.cancelledJobs) 49 | } 50 | 51 | @Test 52 | fun `Coroutines run on main thread`() = runBlocking { 53 | var threads = listOf() 54 | val presenter = FakePresenter( 55 | jobInterceptor = { 56 | threads += Thread.currentThread() 57 | } 58 | ) 59 | presenter.onCreate() 60 | delay(100) 61 | presenter.onDestroy() 62 | delay(100) 63 | threads.forEach { 64 | assert(it.name.startsWith("UIThread")) { "We should switch to UI thread, and now we are on ${it.name}" } 65 | } 66 | } 67 | 68 | @Test 69 | fun `When a job throws an error, it is handled`(): Unit = runBlocking { 70 | val error = Error() 71 | var errors = listOf() 72 | val presenter = FakePresenter( 73 | jobInterceptor = { throw error }, 74 | onError = { errors += it } 75 | ) 76 | presenter.onCreate() 77 | delay(200) 78 | assertEquals(listOf(error, error), errors) 79 | } 80 | } -------------------------------------------------------------------------------- /src/test/java/corotuines/ui/CoroutineExceptionHandlingTest.kt: -------------------------------------------------------------------------------- 1 | package coroutines.ui 2 | 3 | import kotlinx.coroutines.delay 4 | import kotlinx.coroutines.launch 5 | import kotlinx.coroutines.runBlocking 6 | import kotlinx.coroutines.supervisorScope 7 | import org.junit.Test 8 | import kotlin.test.assertEquals 9 | import kotlin.test.assertTrue 10 | 11 | @Suppress("FunctionName") 12 | class CoroutineExceptionHandlingTest { 13 | 14 | class FakePresenterForSingleExceptionHandling(val onSecondAction: ()->Unit) : BasePresenter() { 15 | 16 | var cancelledJobs = 0 17 | 18 | fun onCreate() { 19 | // launch { 20 | // delay(100) 21 | // throw Error() 22 | // } 23 | // launch { 24 | // delay(200) 25 | // onSecondAction() 26 | // } 27 | } 28 | } 29 | 30 | @Test 31 | fun `Error on a single coroutine, does not cancel others`() = runBlocking { 32 | var called = false 33 | val presenter = FakePresenterForSingleExceptionHandling( 34 | onSecondAction = { called = true } 35 | ) 36 | presenter.onCreate() 37 | delay(300) 38 | assertTrue(called) 39 | } 40 | } -------------------------------------------------------------------------------- /src/test/java/delegates/LateinitTest.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("Mutable") 2 | 3 | package delegates 4 | 5 | import org.junit.Test 6 | import java.lang.IllegalArgumentException 7 | import kotlin.properties.ReadWriteProperty 8 | import kotlin.reflect.KProperty 9 | import kotlin.test.assertEquals 10 | import kotlin.test.assertNotNull 11 | 12 | 13 | @Suppress("FunctionName") 14 | class LateinitTest { 15 | 16 | // @Test 17 | // fun `Throws exception if accessed before initialization`() { 18 | // var value: Int by Lateinit() 19 | // val res = runCatching { 20 | // println(value) 21 | // } 22 | // val exception = res.exceptionOrNull() 23 | // assertNotNull(exception) 24 | // assert(exception is IllegalStateException) 25 | // assertEquals("Variable value must be set before it is initialized", exception.message) 26 | // 27 | // var value2: Int by Lateinit() 28 | // val res2 = runCatching { 29 | // println(value2) 30 | // } 31 | // val exception2 = res2.exceptionOrNull() 32 | // assertNotNull(exception2) 33 | // assert(exception2 is IllegalStateException) 34 | // assertEquals("Variable value2 must be set before it is initialized", exception2.message) 35 | // } 36 | // 37 | // @Test 38 | // fun `Behaves like a normal variable for Int`() { 39 | // var value: Int by Lateinit() 40 | // value = 10 41 | // assertEquals(10, value) 42 | // value = 20 43 | // assertEquals(20, value) 44 | // } 45 | // 46 | // @Test 47 | // fun `Behaves like a normal variable for String`() { 48 | // var value: String by Lateinit() 49 | // value = "AAA" 50 | // assertEquals("AAA", value) 51 | // value = "BBB" 52 | // assertEquals("BBB", value) 53 | // } 54 | // 55 | // @Test 56 | // fun `Behaves like a normal variable for nullable String`() { 57 | // var value: String? by Lateinit() 58 | // value = "AAA" 59 | // assertEquals("AAA", value) 60 | // value = null 61 | // assertEquals(null, value) 62 | // value = "BBB" 63 | // assertEquals("BBB", value) 64 | // } 65 | } -------------------------------------------------------------------------------- /src/test/java/delegates/MutableLazyTest.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("Mutable") 2 | 3 | package delegates 4 | 5 | import collections.Student 6 | import org.junit.Assert 7 | import org.junit.Test 8 | import kotlin.properties.ReadWriteProperty 9 | import kotlin.reflect.KProperty 10 | import kotlin.system.measureTimeMillis 11 | 12 | @Suppress("FunctionName") 13 | class MutableLazyTest { 14 | 15 | @Test 16 | fun `Do not initialize if initialized`() { 17 | val time = measureTimeMillis { 18 | var game: Game? by mutableLazy { readGameFromSave() } 19 | game = Game() 20 | print(game) 21 | } 22 | assert(time in 0..100) 23 | } 24 | 25 | @Test 26 | fun `Initializes if not initialized`() { 27 | val time = measureTimeMillis { 28 | val game: Game? by mutableLazy { readGameFromSave() } 29 | print(game) 30 | } 31 | assert(time in 450..550) 32 | } 33 | 34 | @Test 35 | fun `Do not initialize again if already initialized`() { 36 | val time = measureTimeMillis { 37 | val game: Game? by mutableLazy { readGameFromSave() } 38 | print(game) 39 | print(game) 40 | print(game) 41 | } 42 | assert(time in 450..550) 43 | } 44 | 45 | @Test 46 | fun `MutableLazy should accept nullable values`() { 47 | val lazy by mutableLazy { null } 48 | Assert.assertNull(lazy) 49 | 50 | var lazy2 by mutableLazy { "A" } 51 | lazy2 = null 52 | Assert.assertNull(lazy2) 53 | } 54 | 55 | private class Game() 56 | 57 | private fun readGameFromSave(): Game? { 58 | Thread.sleep(500) 59 | return Game() 60 | } 61 | } -------------------------------------------------------------------------------- /src/test/java/dsl/AnnouncementsListTest.kt: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | import kotlin.test.assertNull 6 | 7 | @Suppress("FunctionName") 8 | class AnnouncementsListTest { 9 | 10 | @Test 11 | fun `Reminders have title "Remember!"`() { 12 | val (r1, _, _, r2, _) = getAnnouncements("", "") 13 | assertEquals(r1.title, "Remember!") 14 | assertEquals(r2.title, "Remember!") 15 | } 16 | 17 | @Test 18 | fun `Info without title fills it with null`() { 19 | val (_, _, _, _, info) = getAnnouncements("", "") 20 | assertNull(info.title) 21 | } 22 | 23 | @Test 24 | fun `Whole announcements list is interpreted correctly`() { 25 | val actual = getAnnouncements("passing", "internships") 26 | val expected = listOf( 27 | Announcement("Remember!", "If you want to receive internship, you need to provide documents till end of September"), 28 | Announcement("Students who are passing:", "passing"), 29 | Announcement("Internships:", "internships"), 30 | Announcement("Remember!", "Work hard whole year and prepare to all classes"), 31 | Announcement(null, "Checking this app periodically will help you be up to date with your university") 32 | ) 33 | assertEquals(expected, actual) 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/java/dsl/HtmlDslTest.kt: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | class HtmlDslTest { 7 | 8 | @Test 9 | fun createTableTest() { 10 | @Suppress("SuspiciousCollectionReassignment") 11 | val html = TableBuilder().apply { 12 | trs += TrBuilder().apply { 13 | tds += TdBuilder().apply { text = "A" } 14 | tds += TdBuilder().apply { text = "B" } 15 | } 16 | trs += TrBuilder().apply { 17 | tds += TdBuilder().apply { text = "C" } 18 | tds += TdBuilder().apply { text = "D" } 19 | } 20 | } 21 | assertEquals(html, createTable()) 22 | } 23 | 24 | // @Test 25 | // fun `Table can be created and it is empty by default`() { 26 | // @Suppress("SuspiciousCollectionReassignment") 27 | // val expected = TableBuilder() 28 | // val actual = table {} 29 | // assertEquals(expected, actual) 30 | // } 31 | // 32 | // @Test 33 | // fun `Tr can be created and it is empty`() { 34 | // @Suppress("SuspiciousCollectionReassignment") 35 | // val expected = TableBuilder().apply { 36 | // trs += TrBuilder() 37 | // } 38 | // val actual = table { 39 | // tr {} 40 | // } 41 | // assertEquals(expected, actual) 42 | // } 43 | // 44 | // @Test 45 | // fun `Multiple tr can be created`() { 46 | // @Suppress("SuspiciousCollectionReassignment") 47 | // val expected = TableBuilder().apply { 48 | // trs += TrBuilder() 49 | // trs += TrBuilder() 50 | // } 51 | // val actual = table { 52 | // tr {} 53 | // tr {} 54 | // } 55 | // assertEquals(expected, actual) 56 | // } 57 | // 58 | // @Test 59 | // fun `Td can be created and it is empty`() { 60 | // @Suppress("SuspiciousCollectionReassignment") 61 | // val expected = TableBuilder().apply { 62 | // trs += TrBuilder().apply { 63 | // tds += TdBuilder() 64 | // } 65 | // } 66 | // val actual = table { 67 | // tr { 68 | // td {} 69 | // } 70 | // } 71 | // assertEquals(expected, actual) 72 | // } 73 | // 74 | // @Test 75 | // fun dslTestStandard() { 76 | // @Suppress("SuspiciousCollectionReassignment") 77 | // val expected = TableBuilder().apply { 78 | // trs += TrBuilder().apply { 79 | // tds += TdBuilder().apply { text = "A" } 80 | // tds += TdBuilder().apply { text = "B" } 81 | // } 82 | // trs += TrBuilder().apply { 83 | // tds += TdBuilder().apply { text = "C" } 84 | // tds += TdBuilder().apply { text = "D" } 85 | // } 86 | // } 87 | // val actual = table { 88 | // tr { 89 | // td { +"A" } 90 | // td { +"B" } 91 | // } 92 | // tr { 93 | // td { +"C" } 94 | // td { +"D" } 95 | // } 96 | // } 97 | // assertEquals(expected, actual) 98 | // } 99 | // 100 | // @Test 101 | // fun dslTestMoreColons() { 102 | // @Suppress("SuspiciousCollectionReassignment") 103 | // val expected = TableBuilder().apply { 104 | // trs += TrBuilder().apply { 105 | // tds += TdBuilder().apply { text = "A" } 106 | // tds += TdBuilder().apply { text = "B" } 107 | // tds += TdBuilder().apply { text = "C" } 108 | // } 109 | // trs += TrBuilder().apply { 110 | // tds += TdBuilder().apply { text = "C" } 111 | // tds += TdBuilder().apply { text = "D" } 112 | // } 113 | // } 114 | // val actual = table { 115 | // tr { 116 | // td { +"A" } 117 | // td { +"B" } 118 | // td { +"C" } 119 | // } 120 | // tr { 121 | // td { +"C" } 122 | // td { +"D" } 123 | // } 124 | // } 125 | // assertEquals(expected, actual) 126 | // } 127 | } -------------------------------------------------------------------------------- /src/test/java/dsl/UsersTableTest.kt: -------------------------------------------------------------------------------- 1 | package dsl 2 | 3 | import org.junit.Test 4 | import org.junit.jupiter.api.Assertions.* 5 | 6 | class UsersTableTest { 7 | @Test 8 | fun `should work for empty list`() { 9 | // when 10 | val result = usersTable(listOf()) 11 | 12 | // then 13 | val expected = TableBuilder().apply { 14 | trs += TrBuilder().apply { 15 | tds += TdBuilder().apply { text = "Id" } 16 | tds += TdBuilder().apply { text = "Name" } 17 | tds += TdBuilder().apply { text = "Points" } 18 | tds += TdBuilder().apply { text = "Category" } 19 | } 20 | } 21 | assertEquals(expected, result) 22 | } 23 | 24 | @Test 25 | fun `should work for a list with a single element`() { 26 | // given 27 | val users = listOf( 28 | User("1", "Randy", 2, "A"), 29 | ) 30 | 31 | // when 32 | val result = usersTable(users) 33 | 34 | // then 35 | val expected = TableBuilder().apply { 36 | trs += TrBuilder().apply { 37 | tds += TdBuilder().apply { text = "Id" } 38 | tds += TdBuilder().apply { text = "Name" } 39 | tds += TdBuilder().apply { text = "Points" } 40 | tds += TdBuilder().apply { text = "Category" } 41 | } 42 | trs += TrBuilder().apply { 43 | tds += TdBuilder().apply { text = "1" } 44 | tds += TdBuilder().apply { text = "Randy" } 45 | tds += TdBuilder().apply { text = "2" } 46 | tds += TdBuilder().apply { text = "A" } 47 | } 48 | } 49 | assertEquals(expected, result) 50 | } 51 | 52 | @Test 53 | fun `should work for a list with multiple users`() { 54 | // given 55 | val users = listOf( 56 | User("1", "Randy", 2, "A"), 57 | User("4", "Andy", 4, "B"), 58 | User("3", "Mandy", 1, "C"), 59 | User("5", "Cindy", 5, "A"), 60 | User("2", "Lindy", 3, "B"), 61 | ) 62 | 63 | // when 64 | val result = usersTable(users) 65 | 66 | // then 67 | val expected = TableBuilder().apply { 68 | trs += TrBuilder().apply { 69 | tds += TdBuilder().apply { text = "Id" } 70 | tds += TdBuilder().apply { text = "Name" } 71 | tds += TdBuilder().apply { text = "Points" } 72 | tds += TdBuilder().apply { text = "Category" } 73 | } 74 | for (user in users) { 75 | trs += TrBuilder().apply { 76 | tds += TdBuilder().apply { text = user.id } 77 | tds += TdBuilder().apply { text = user.name } 78 | tds += TdBuilder().apply { text = user.points.toString() } 79 | tds += TdBuilder().apply { text = user.category } 80 | } 81 | } 82 | } 83 | assertEquals(expected, result) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/extra/PermutationTest.kt: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import basics.factorial 4 | import functional.product 5 | import org.junit.Test 6 | import kotlin.test.assertEquals 7 | 8 | @Suppress("FunctionName") 9 | internal class PermutationTest { 10 | 11 | @Test 12 | fun `Test permutation numbers for Sets with different sizes`() { 13 | val setSizeToPermutations = mapOf( 14 | 0 to 1L, 15 | 1 to 1L, 16 | 2 to 2L, 17 | 3 to 6L, 18 | 4 to 24L 19 | ) 20 | for ((setSize, expectedNumber) in setSizeToPermutations) { 21 | val set = (1..setSize).toSet() 22 | assertEquals(expectedNumber, set.permutationsNumber()) 23 | } 24 | } 25 | 26 | @Test 27 | fun `Test permutation numbers for Lists with different sizes and different elements`() { 28 | val listSizeToPermutations = mapOf( 29 | 0 to 1L, 30 | 1 to 1L, 31 | 2 to 2L, 32 | 3 to 6L, 33 | 4 to 24L 34 | ) 35 | for ((listSize, expectedNumber) in listSizeToPermutations) { 36 | val set = (1..listSize).toList() 37 | assertEquals(expectedNumber, set.permutationsNumber()) 38 | } 39 | } 40 | 41 | @Test 42 | fun `Test permutation numbers for Lists with different sizes and same elements`() { 43 | val listToPermutations = mapOf( 44 | listOf(1, 1, 1, 1) to 1L, 45 | listOf(1, 1, 2, 2) to 6L, 46 | listOf(1, 1, 1, 2) to 4L 47 | ) 48 | for ((list, expectedNumber) in listToPermutations) { 49 | assertEquals(expectedNumber, list.permutationsNumber()) 50 | } 51 | } 52 | 53 | @Test 54 | fun `Get all permutations for different elements`() { 55 | val setToPermutations = mapOf( 56 | setOf() to setOf>(), 57 | setOf(1, 2) to setOf(listOf(1, 2), listOf(2, 1)), 58 | setOf(1, 2, 3) to setOf(listOf(1, 2, 3), listOf(2, 1, 3), listOf(1, 3, 2), listOf(2, 3, 1), listOf(3, 1, 2), listOf(3, 2, 1)) 59 | ) 60 | for ((set, allExpectedPermutations) in setToPermutations) { 61 | assertEquals(allExpectedPermutations, set.permutations()) 62 | } 63 | } 64 | 65 | @Test 66 | fun `Check permutations number in allPermutations for bigger numbers`() { 67 | val set = (1..5).toSet() 68 | assertEquals(set.permutationsNumber(), set.permutations().size.toLong()) 69 | } 70 | } -------------------------------------------------------------------------------- /src/test/java/extra/PowersetTest.kt: -------------------------------------------------------------------------------- 1 | package extra 2 | 3 | import kotlin.test.assertEquals 4 | import org.junit.Test 5 | 6 | @Suppress("FunctionName") 7 | class PowersetTest { 8 | 9 | @Test 10 | fun `Powerset of empty list is only empty list`() { 11 | val emptyList = setOf() 12 | assertEquals(setOf(emptyList), emptyList.powerset()) 13 | } 14 | 15 | @Test 16 | fun `Powerset of list with single element is empty list and single element`() { 17 | assertEquals(setOf(setOf(1), setOf()), setOf(1).powerset()) 18 | } 19 | 20 | @Test 21 | fun `Powerset simple example test`() { 22 | val set = setOf( 23 | setOf(1, 2, 3), 24 | setOf(1, 2), 25 | setOf(1, 3), 26 | setOf(2, 3), 27 | setOf(1), 28 | setOf(2), 29 | setOf(3), 30 | setOf()) 31 | assertEquals(set, setOf(1, 2, 3).powerset()) 32 | } 33 | 34 | @Test 35 | fun `Size of n element set powerset is 2^n`() { 36 | for(n in 1..6) { 37 | val set = (1..n).toSet() 38 | val size = 2.pow(n) 39 | assertEquals(size, set.powerset().size) 40 | } 41 | } 42 | 43 | private fun Int.pow(power: Int): Int = Math.pow(this.toDouble(), power.toDouble()).toInt() 44 | } -------------------------------------------------------------------------------- /src/test/java/functional/CallbacksTest.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | import org.junit.Test 4 | import kotlin.concurrent.thread 5 | import kotlin.test.assertEquals 6 | import kotlin.test.assertTrue 7 | 8 | @Suppress("FunctionName") 9 | internal class CallbacksTest { 10 | private val user1 = User("AAA", 123) 11 | private val user2 = User("BBB", 1) 12 | private val repo1 = Repo(10, "R1") 13 | private val repo2 = Repo(11, "R2") 14 | 15 | @Test 16 | fun `Function works without errors`() { 17 | getAggregatedContributions(EmptyService) {} 18 | } 19 | 20 | @Test 21 | fun `When no repositories or no users, returns empty lists`() { 22 | var times = 0 23 | getAggregatedContributions(EmptyService) { 24 | times++ 25 | } 26 | assertEquals(times, 1) 27 | getAggregatedContributions(FakeStaticSyncService(listOf(), listOf(user1))) { 28 | assertEquals(listOf(), it) 29 | times++ 30 | } 31 | assertEquals(times, 2) 32 | getAggregatedContributions(FakeStaticSyncService(listOf(repo1), listOf())) { 33 | assertEquals(listOf(), it) 34 | times++ 35 | } 36 | assertEquals(times, 3) 37 | } 38 | 39 | @Test 40 | fun `Lists all unique users`() { 41 | var called = false 42 | getAggregatedContributions(FakeStaticSyncService(listOf(repo1), listOf(user1, user2))) { 43 | assertEquals(listOf(user1, user2), it) 44 | called = true 45 | } 46 | assertTrue(called) 47 | } 48 | 49 | @Test 50 | fun `Accumulates contributions of a single user`() { 51 | var called = false 52 | getAggregatedContributions(FakeStaticSyncService(listOf(repo1), listOf(user1, user1))) { 53 | assertEquals(listOf(User(user1.login, user1.contributions * 2)), it) 54 | called = true 55 | } 56 | assertTrue(called) 57 | } 58 | 59 | @Test 60 | fun `Accumulates contributions of multiple users user`() { 61 | var called = false 62 | getAggregatedContributions(FakeStaticSyncService(listOf(repo1, repo2), listOf(user1, user1, user2))) { 63 | val expected = listOf( 64 | User(user1.login, user1.contributions * 4), 65 | User(user2.login, user2.contributions * 2) 66 | ).sortedBy { it.contributions } 67 | assertEquals(expected, it.sortedBy { it.contributions }) 68 | called = true 69 | } 70 | assertTrue(called) 71 | } 72 | 73 | @Test 74 | fun `Prepared for multithreading`() { 75 | val service = FakeDelayedAsyncService(List(100) { repo1 }, List(100) { user1 }) 76 | var res = listOf() 77 | getAggregatedContributions(service) { res = it } 78 | Thread.sleep(500) 79 | assertEquals(listOf(User(user1.login, user1.contributions * 100 * 100)), res) 80 | } 81 | 82 | class FakeStaticSyncService(private val repos: List, private val users: List) : GitHubService { 83 | override fun getOrgRepos(callback: (List) -> Unit) { 84 | callback(repos) 85 | } 86 | 87 | override fun getRepoContributors(repo: String, callback: (List) -> Unit) { 88 | callback(users) 89 | } 90 | } 91 | 92 | class FakeDelayedAsyncService(private val repos: List, private val users: List) : GitHubService { 93 | 94 | override fun getOrgRepos(callback: (List) -> Unit) { 95 | thread { 96 | Thread.sleep(DELAY_TIME_MS) 97 | callback(repos) 98 | } 99 | } 100 | 101 | override fun getRepoContributors(repo: String, callback: (List) -> Unit) { 102 | thread { 103 | Thread.sleep(DELAY_TIME_MS) 104 | callback(users) 105 | } 106 | } 107 | 108 | companion object { 109 | val DELAY_TIME_MS = 30L 110 | } 111 | } 112 | 113 | object EmptyService : GitHubService { 114 | override fun getOrgRepos(callback: (List) -> Unit) { 115 | callback(emptyList()) 116 | } 117 | 118 | override fun getRepoContributors(repo: String, callback: (List) -> Unit) { 119 | callback(emptyList()) 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /src/test/java/functional/FunctionalTest.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | class FunctionalTest { 7 | 8 | @Test 9 | fun testLambdaFunctionalTypeSpecified() { 10 | testFunctions(LambdaFunctionalTypeSpecified()) 11 | } 12 | 13 | @Test 14 | fun testLambdaFunctionalTypeInferred() { 15 | testFunctions(LambdaFunctionalTypeInferred()) 16 | } 17 | 18 | @Test 19 | fun testAnonymousFunctionalTypeSpecified() { 20 | testFunctions(AnonymousFunctionalTypeSpecified()) 21 | } 22 | 23 | @Test 24 | fun testAnonymousFunctionalTypeInferred() { 25 | testFunctions(AnonymousFunctionalTypeInferred()) 26 | } 27 | 28 | @Test 29 | fun testFunctionReference() { 30 | testFunctions(FunctionReference()) 31 | } 32 | 33 | @Test 34 | fun testFunctionMemberReference() { 35 | testFunctions(FunctionMemberReference()) 36 | } 37 | 38 | @Test 39 | fun testBoundedFunctionReference() { 40 | testFunctions(BoundedFunctionReference()) 41 | } 42 | 43 | fun testFunctions(obj: FunctionsFunctional) { 44 | assertEquals(3, (obj.add as (Int, Int)->Int)(1, 2)) 45 | assertEquals(6, (obj.triple as (Int)->Int)(2)) 46 | assertEquals("BBB", (obj.longestOf as (String, String, String)->String)("AA", "BBB", "CC")) 47 | assertEquals("AA", (obj.longestOf as (String, String, String)->String)("AA", "B", "C")) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/functional/ObservableValueTest.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | class ObservableValue(initial: T) { 7 | var value: T = initial 8 | 9 | fun observe(observer: (T) -> Unit) { 10 | TODO() 11 | } 12 | } 13 | 14 | class ObservableValueTest { 15 | 16 | @Test 17 | fun `should behave like regular value`() { 18 | val i = ObservableValue(null) 19 | assertEquals(null, i.value) 20 | i.value = 1 21 | assertEquals(1, i.value) 22 | i.value = 2 23 | assertEquals(2, i.value) 24 | } 25 | 26 | @Test 27 | fun `should call observer when value changes`() { 28 | val o = ObservableValue("A") 29 | val called = mutableListOf() 30 | o.observe { called += it } 31 | o.value = "B" 32 | o.value = "C" 33 | o.value = "D" 34 | assertEquals(listOf("B", "C", "D"), called) 35 | } 36 | 37 | @Test 38 | fun `should call observers when value changes`() { 39 | val o1 = ObservableValue("A") 40 | val o2 = ObservableValue("A") 41 | val history1 = mutableListOf() 42 | o1.observe { history1 += it } 43 | o1.value = "B" 44 | val history2 = mutableListOf() 45 | o1.observe { history2 += it } 46 | val history3 = mutableListOf() 47 | o2.observe { history3 += it } 48 | o1.value = "C" 49 | o1.value = "D" 50 | assertEquals(listOf("B", "C", "D"), history1) 51 | assertEquals(listOf("C", "D"), history2) 52 | assertEquals(listOf(), history3) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/functional/ProductTest.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | @Suppress("FunctionName") 7 | internal class ProductTest { 8 | 9 | @Test 10 | fun `Product of empty IntRange is 1`() { 11 | @Suppress("EmptyRange") 12 | assertEquals(1, (0..-1).product()) 13 | } 14 | 15 | @Test 16 | fun `Test products of different IntRanges`() { 17 | val rangeToProduct = mapOf( 18 | 2..4 to 24L, 19 | 1..4 to 24L, 20 | 3..4 to 12L, 21 | 100..100 to 100L 22 | ) 23 | for ((range, product) in rangeToProduct) 24 | assertEquals(product, range.product()) 25 | } 26 | 27 | @Test 28 | fun `Test products of different Int Collections`() { 29 | val collectionToProduct = mapOf( 30 | listOf(1,2,3) to 6L, 31 | listOf(2,3) to 6L, 32 | listOf(3) to 3L, 33 | listOf(10, 10, 10) to 1000L 34 | ) 35 | for ((collection, product) in collectionToProduct) 36 | assertEquals(product, collection.product()) 37 | } 38 | } -------------------------------------------------------------------------------- /src/test/java/functional/RationalTest.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | class RationalTest { 7 | @Test 8 | fun testIntExtension() { 9 | assertEquals(Rational(4, 1), 4.r()) 10 | for (i in 1..100) { 11 | assertEquals(Rational(i, 1), i.r()) 12 | } 13 | } 14 | 15 | @Test 16 | fun testPairExtension() { 17 | assertEquals(Rational(2, 3), Pair(2, 3).r()) 18 | 19 | for (l in 1..10) { 20 | for (r in 1..10) { 21 | val p = Pair(l, r) 22 | assertEquals(Rational(l, r), p.r()) 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/functional/RepeatTest.kt: -------------------------------------------------------------------------------- 1 | package functional 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | import kotlin.repeat as `Do not use it` 6 | 7 | class RepeatTest { 8 | 9 | @Test 10 | fun add3Str() { 11 | var str = "" 12 | repeat(3) { 13 | str += "A" 14 | } 15 | Assert.assertEquals("AAA", str) 16 | } 17 | 18 | @Test 19 | fun add3Int() { 20 | var i = 0 21 | repeat(3) { 22 | i++ 23 | } 24 | Assert.assertEquals(3, i) 25 | } 26 | 27 | @Test 28 | fun add0Int() { 29 | var i = 0 30 | repeat(0) { 31 | i++ 32 | } 33 | Assert.assertEquals(0, i) 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/java/nullability/Task.kt: -------------------------------------------------------------------------------- 1 | package nullability 2 | 3 | import org.junit.Test 4 | import kotlin.test.assertEquals 5 | 6 | @Suppress("FunctionName") 7 | internal class NullabilityTaskTests { 8 | 9 | class MailCollector(): Mailer { 10 | data class Mail(val email: String, val message: String) 11 | 12 | var emails = listOf() 13 | 14 | override fun sendMessage(email: String, message: String) { 15 | emails += Mail(email, message) 16 | } 17 | } 18 | 19 | @Test 20 | fun `When client is null, email we do not send email`() { 21 | val mailer = MailCollector() 22 | sendMessageToClient(null, "AAA", mailer) 23 | assertEquals(emptyList(), mailer.emails) 24 | } 25 | 26 | @Test 27 | fun `When message is null, we do not send email`() { 28 | val mailer = MailCollector() 29 | sendMessageToClient(Client(PersonalInfo("AAA")), null, mailer) 30 | assertEquals(emptyList(), mailer.emails) 31 | } 32 | 33 | @Test 34 | fun `When personal info is null, we do not send email`() { 35 | val mailer = MailCollector() 36 | sendMessageToClient(Client(null), "AAA", mailer) 37 | assertEquals(emptyList(), mailer.emails) 38 | } 39 | 40 | @Test 41 | fun `When email address is null, we do not send email`() { 42 | val mailer = MailCollector() 43 | sendMessageToClient(Client(PersonalInfo(null)), null, mailer) 44 | assertEquals(emptyList(), mailer.emails) 45 | } 46 | 47 | @Test 48 | fun `Sends messages correctly for correct values`() { 49 | val mailer = MailCollector() 50 | sendMessageToClient(Client(PersonalInfo("AAA")), "BBB", mailer) 51 | assertEquals(listOf(MailCollector.Mail("AAA", "BBB")), mailer.emails) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/nullability/TaskThrowing.kt: -------------------------------------------------------------------------------- 1 | package nullability 2 | 3 | import junit.framework.Assert.* 4 | import org.junit.* 5 | 6 | class NullabilityTaskThrowingTests { 7 | 8 | class MailCollector() : Mailer { 9 | data class Mail(val email: String, val message: String) 10 | 11 | var emails = listOf() 12 | 13 | override fun sendMessage(email: String, message: String) { 14 | emails += Mail(email, message) 15 | } 16 | } 17 | 18 | @Test 19 | fun `When client is null, email is not sent`() { 20 | val mailer = MailCollector() 21 | val res = runCatching { sendMessageToClient(null, "AAA", mailer) } 22 | val exception = res.exceptionOrNull() 23 | assert(exception != null) { "Exception not thrown" } 24 | assert(exception is IllegalArgumentException) { "Exception is $exception, and it should be of type IllegalArgumentException" } 25 | assertEquals(emptyList(), mailer.emails) 26 | } 27 | 28 | @Test 29 | fun `When message is null, email is not sent`() { 30 | val mailer = MailCollector() 31 | val res = runCatching { sendMessageToClient(Client(PersonalInfo("AAA")), null, mailer) } 32 | val exception = res.exceptionOrNull() 33 | assert(exception != null) { "Exception not thrown" } 34 | assert(exception is IllegalArgumentException) { "Exception is $exception, and it should be of type IllegalArgumentException" } 35 | assertEquals(emptyList(), mailer.emails) 36 | } 37 | 38 | @Test 39 | fun `When personal info is null, email is not sent`() { 40 | val mailer = MailCollector() 41 | val res = runCatching { sendMessageToClient(Client(null), "AAA", mailer) } 42 | val exception = res.exceptionOrNull() 43 | assert(exception != null) { "Exception not thrown" } 44 | assert(exception is IllegalArgumentException) { "Exception is $exception, and it should be of type IllegalArgumentException" } 45 | assertEquals(emptyList(), mailer.emails) 46 | } 47 | 48 | @Test 49 | fun `When email is null, email is not sent`() { 50 | val mailer = MailCollector() 51 | val res = runCatching { sendMessageToClient(Client(PersonalInfo(null)), null, mailer) } 52 | val exception = res.exceptionOrNull() 53 | assert(exception != null) { "Exception not thrown" } 54 | assert(exception is IllegalArgumentException) { "Exception is $exception, and it should be of type IllegalArgumentException" } 55 | assertEquals(emptyList(), mailer.emails) 56 | } 57 | 58 | @Test 59 | fun `Sends messages correctly`() { 60 | val mailer = MailCollector() 61 | sendMessageToClient(Client(PersonalInfo("AAA")), "BBB", mailer) 62 | assertEquals(listOf(MailCollector.Mail("AAA", "BBB")), mailer.emails) 63 | } 64 | } 65 | --------------------------------------------------------------------------------