diff --git a/README.MD b/README.MD index cf843c1..770d065 100644 --- a/README.MD +++ b/README.MD @@ -55,6 +55,8 @@ Since I'm always working on some side-projects, I decided to document the develo - Ep.13: https://github.com/JoaquimLey/transport-eta/pull/64 +- Ep.14: https://github.com/JoaquimLey/transport-eta/pull/65 + ## About the author Hi, my name is **Joaquim Ley**, I'm a Software Engineer (Android). diff --git a/transport-eta-android/data/build.gradle b/transport-eta-android/data/build.gradle new file mode 100644 index 0000000..2280f27 --- /dev/null +++ b/transport-eta-android/data/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'kotlin' + +// Module configuration +configurations.all { + resolutionStrategy { + force deps.kotlin.stdlib + } +} + + +dependencies { + // Modules + implementation project(':domain') + + // Javax + implementation deps.javax.inject + compileOnly deps.javax.annotation + // Kotlin + implementation deps.kotlin.rx + implementation deps.kotlin.stdlib + + /*********** + * Testing * + ***********/ + + // Testing + testImplementation deps.junit + testImplementation deps.mockito.kotlin + testImplementation deps.mockito.inline +} \ No newline at end of file diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/data/FavoritesRepositoryImpl.kt b/transport-eta-android/data/src/main/java/com/joaquimley/data/FavoritesRepositoryImpl.kt new file mode 100644 index 0000000..fe77a38 --- /dev/null +++ b/transport-eta-android/data/src/main/java/com/joaquimley/data/FavoritesRepositoryImpl.kt @@ -0,0 +1,29 @@ +package com.joaquimley.data + +import com.joaquimley.data.mapper.TransportMapper +import com.joaquimley.data.store.TransportDataStore +import com.joaquimley.transporteta.domain.model.Transport +import com.joaquimley.transporteta.domain.repository.FavoritesRepository +import io.reactivex.Completable +import io.reactivex.Flowable + +class FavoritesRepositoryImpl(private val transportDataStore: TransportDataStore, + private val mapper: TransportMapper) : FavoritesRepository { + + override fun markAsFavorite(transport: Transport): Completable { + return transportDataStore.markAsFavorite(mapper.toEntity(transport)) + } + + override fun removeAsFavorite(transport: Transport): Completable { + return transportDataStore.removeAsFavorite(mapper.toEntity(transport)) + } + + override fun getAll(): Flowable> { + return transportDataStore.getAllFavorites().map { mapper.toModel(it) } + } + + override fun clearAll(): Completable { + return transportDataStore.clearAllFavorites() + } + +} \ No newline at end of file diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/data/TransportRepositoryImpl.kt b/transport-eta-android/data/src/main/java/com/joaquimley/data/TransportRepositoryImpl.kt new file mode 100644 index 0000000..42eca01 --- /dev/null +++ b/transport-eta-android/data/src/main/java/com/joaquimley/data/TransportRepositoryImpl.kt @@ -0,0 +1,38 @@ +package com.joaquimley.data + +import com.joaquimley.transporteta.domain.model.Transport +import com.joaquimley.transporteta.domain.repository.TransportRepository +import io.reactivex.Completable +import io.reactivex.Observable + +class TransportRepositoryImpl: TransportRepository { + + override fun requestTransportEta(transportCode: Int): Observable { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun cancelTransportEtaRequest(transportCode: Int?): Completable { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun getTransport(transportId: String): Observable { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun saveTransport(transport: Transport): Completable { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun saveTransport(transportList: List): Completable { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun deleteTransport(transport: Transport): Completable { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun deleteTransports(transport: List): Completable { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/data/executor/ThreadExecutorImpl.kt b/transport-eta-android/data/src/main/java/com/joaquimley/data/executor/ThreadExecutorImpl.kt new file mode 100644 index 0000000..abc1864 --- /dev/null +++ b/transport-eta-android/data/src/main/java/com/joaquimley/data/executor/ThreadExecutorImpl.kt @@ -0,0 +1,48 @@ +package com.joaquimley.data.executor + +import com.joaquimley.transporteta.domain.executor.ThreadExecutor +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.ThreadFactory +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit + +class ThreadExecutorImpl(private val threadFactory: ThreadFactory = JobThreadFactory(), + private val workQueue: LinkedBlockingQueue = LinkedBlockingQueue(), + private val threadPoolExecutor: ThreadPoolExecutor = + ThreadPoolExecutor(INITIAL_POOL_SIZE, MAX_POOL_SIZE, + KEEP_ALIVE_TIME.toLong(), KEEP_ALIVE_TIME_UNIT, + workQueue, threadFactory)) + : ThreadExecutor { + + + override fun execute(runnable: Runnable?) { + runnable?.let { threadPoolExecutor.execute(runnable) } + ?: run { + throw IllegalArgumentException("Runnable to execute cannot be null") + } + } + + private class JobThreadFactory : ThreadFactory { + private var counter = 0 + + override fun newThread(runnable: Runnable): Thread { + return Thread(runnable, THREAD_NAME + counter++) + } + + companion object { + private const val THREAD_NAME = "transport_eta_" + } + } + + companion object { + + private const val INITIAL_POOL_SIZE = 3 // TODO: Get number of CPU cores + private const val MAX_POOL_SIZE = 5 // TODO: Get number of CPU cores + + // Sets the amount of time an idle thread waits before terminating + private const val KEEP_ALIVE_TIME = 10 + + // Sets the Time Unit to seconds + private val KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS + } +} \ No newline at end of file diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/data/mapper/TransportMapper.kt b/transport-eta-android/data/src/main/java/com/joaquimley/data/mapper/TransportMapper.kt new file mode 100644 index 0000000..2c12a3d --- /dev/null +++ b/transport-eta-android/data/src/main/java/com/joaquimley/data/mapper/TransportMapper.kt @@ -0,0 +1,24 @@ +package com.joaquimley.data.mapper + +import com.joaquimley.data.model.TransportEntity +import com.joaquimley.transporteta.domain.model.Transport + +class TransportMapper { + + fun toEntity(from: List): List { + return from.map { toEntity(it) } + } + + fun toEntity(from: Transport): TransportEntity { + return TransportEntity(from.id, from.name, from.code, from.latestEta, from.isFavorite, from.type) + } + + fun toModel(from: List): List { + return from.map { toModel(it) } + } + + fun toModel(from: TransportEntity): Transport { + return Transport(from.id, from.name, from.code, from.latestEta, from.isFavorite, from.type) + } + +} \ No newline at end of file diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/data/model/TransportEntity.kt b/transport-eta-android/data/src/main/java/com/joaquimley/data/model/TransportEntity.kt new file mode 100644 index 0000000..c48dfb5 --- /dev/null +++ b/transport-eta-android/data/src/main/java/com/joaquimley/data/model/TransportEntity.kt @@ -0,0 +1,4 @@ +package com.joaquimley.data.model + +data class TransportEntity(val id: String, val name: String, val code: Int, val latestEta: String, + val isFavorite: Boolean = false, val type: String) diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/data/source/SharedPreferencesDataSource.kt b/transport-eta-android/data/src/main/java/com/joaquimley/data/source/SharedPreferencesDataSource.kt new file mode 100644 index 0000000..8f06f2e --- /dev/null +++ b/transport-eta-android/data/src/main/java/com/joaquimley/data/source/SharedPreferencesDataSource.kt @@ -0,0 +1,3 @@ +package com.joaquimley.data.source + +class SharedPreferencesDataSource \ No newline at end of file diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/data/store/TransportDataStore.kt b/transport-eta-android/data/src/main/java/com/joaquimley/data/store/TransportDataStore.kt new file mode 100644 index 0000000..09a2353 --- /dev/null +++ b/transport-eta-android/data/src/main/java/com/joaquimley/data/store/TransportDataStore.kt @@ -0,0 +1,35 @@ +package com.joaquimley.data.store + +import com.joaquimley.data.model.TransportEntity +import io.reactivex.Completable +import io.reactivex.Flowable +import io.reactivex.Observable + +interface TransportDataStore { + + fun markAsFavorite(transport: TransportEntity): Completable + + fun removeAsFavorite(transport: TransportEntity): Completable + + fun getAllFavorites(): Flowable> + + fun clearAllFavorites(): Completable + + fun getAll(): Flowable> + + fun getTransport(transportId: String): Observable + + fun saveTransport(transport: TransportEntity): Completable + + fun saveTransport(transportList: List): Completable + + fun deleteTransport(transport: TransportEntity): Completable + + fun deleteTransport(transport: List): Completable + + // TODO Should these be in the [SmSController] instead? [RequestEtaUseCase] + fun requestTransportEta(transportCode: Int): Observable + + // TODO Should these be in the [SmSController] instead ? [RequestEtaUseCase] + fun cancelTransportEtaRequest(transportCode: Int?): Completable +} \ No newline at end of file diff --git a/transport-eta-android/data/src/main/java/com/joaquimley/data/store/TransportDataStoreImpl.kt b/transport-eta-android/data/src/main/java/com/joaquimley/data/store/TransportDataStoreImpl.kt new file mode 100644 index 0000000..c3d8234 --- /dev/null +++ b/transport-eta-android/data/src/main/java/com/joaquimley/data/store/TransportDataStoreImpl.kt @@ -0,0 +1,7 @@ +package com.joaquimley.data.store + +import com.joaquimley.data.source.SharedPreferencesDataSource + +class TransportDataStoreImpl(val sharedPreferencesDataSource: SharedPreferencesDataSource) { + +} \ No newline at end of file diff --git a/transport-eta-android/data/src/test/java/com/joaquimley/data/FavoritesRepositoryTest.kt b/transport-eta-android/data/src/test/java/com/joaquimley/data/FavoritesRepositoryTest.kt new file mode 100644 index 0000000..271ed6c --- /dev/null +++ b/transport-eta-android/data/src/test/java/com/joaquimley/data/FavoritesRepositoryTest.kt @@ -0,0 +1,282 @@ +package com.joaquimley.data + +import com.joaquimley.data.factory.DataFactory.Factory.randomUuid +import com.joaquimley.data.factory.TransportFactory +import com.joaquimley.data.mapper.TransportMapper +import com.joaquimley.data.model.TransportEntity +import com.joaquimley.data.store.TransportDataStore +import com.joaquimley.transporteta.domain.model.Transport +import com.nhaarman.mockitokotlin2.* +import io.reactivex.Completable +import io.reactivex.Flowable +import org.junit.After +import org.junit.Before +import org.junit.Test + +class FavoritesRepositoryTest { + + private val robot = Robot() + + private val mockTransportDataStore = mock() + private val mockMapper = mock() + + private lateinit var favoritesRepository: FavoritesRepositoryImpl + + + @Before + fun setUp() { + favoritesRepository = FavoritesRepositoryImpl(mockTransportDataStore, mockMapper) + } + + @After + fun tearDown() { + } + + // region markAsFavorite + + @Test + fun markAsFavoriteCompletes() { + // Assemble + val transport = TransportFactory.makeTransport() + val transportEntity = TransportFactory.makeTransportEntity() + robot.stubTransportMapperToEntity(transport, transportEntity) + robot.stubDataStoreMarkAsFavoriteSuccess(transportEntity) + // Act + val testObserver = favoritesRepository.markAsFavorite(transport).test() + // Assert + testObserver.assertComplete() + } + + @Test + fun markAsFavoriteCallsCorrectTransportDataStoreMethod() { + // Assemble + val transport = TransportFactory.makeTransport() + val transportEntity = TransportFactory.makeTransportEntity() + robot.stubTransportMapperToEntity(transport, transportEntity) + // Act + favoritesRepository.markAsFavorite(transport) + // Assert + verify(mockTransportDataStore, times(1)).markAsFavorite(transportEntity) + } + + @Test + fun markAsFavoriteFailsThrowsException() { + // Assemble + val transport = TransportFactory.makeTransport() + val transportEntity = TransportFactory.makeTransportEntity() + robot.stubTransportMapperToEntity(transport, transportEntity) + + val errorMessage = "Testing mark As Favorite failed" + robot.stubDataStoreMarkAsFavoriteFails(errorMessage) + // Act + val testObserver = favoritesRepository.markAsFavorite(transport).test() + // Assert + testObserver.assertFailureAndMessage(Throwable::class.java, errorMessage) + } + + // endregion markAsFavorite + + // region removeAsFavorite + + @Test + fun removeAsFavoriteCompletes() { + // Assemble + val transport = TransportFactory.makeTransport() + val transportEntity = TransportFactory.makeTransportEntity() + robot.stubTransportMapperToEntity(transport, transportEntity) + robot.stubDataStoreRemoveAsFavoriteSuccess(transportEntity) + // Act + val testObserver = favoritesRepository.removeAsFavorite(transport).test() + // Assert + testObserver.assertComplete() + } + + @Test + fun removeAsFavoriteCallsCorrectTransportDataStoreMethod() { + // Assemble + val transport = TransportFactory.makeTransport() + val transportEntity = TransportFactory.makeTransportEntity() + robot.stubTransportMapperToEntity(transport, transportEntity) + // Act + favoritesRepository.removeAsFavorite(transport) + // Assert + verify(mockTransportDataStore, times(1)).removeAsFavorite(transportEntity) + } + + @Test + fun removeAsFavoriteFailsThrowsException() { + // Assemble + val transport = TransportFactory.makeTransport() + val transportEntity = TransportFactory.makeTransportEntity() + robot.stubTransportMapperToEntity(transport, transportEntity) + + val errorMessage = "Testing removeAsFavorite failed" + robot.stubDataStoreRemoveAsFavoriteFails(errorMessage) + // Act + val testObserver = favoritesRepository.removeAsFavorite(transport).test() + // Assert + testObserver.assertFailureAndMessage(Throwable::class.java, errorMessage) + } + + // endregion removeAsFavorite + + // region getAllFavorites + + @Test + fun getAllFavoritesCompletes() { + // Assemble + val transportList = TransportFactory.makeTransportList(5, true) + val transportEntityList = TransportFactory.makeTransportEntityList(5, true) + robot.stubTransportMapperToModel(transportEntityList, transportList) + robot.stubDataStoreGetAllFavoritesSuccess(transportEntityList) + // Act + val testObserver = favoritesRepository.getAll().test() + // Assert + testObserver.assertComplete() + } + + @Test + fun getAllFavoritesCallsCorrectTransportDataStoreMethod() { + // Assemble + val transportList = TransportFactory.makeTransportList(5, true) + val transportEntityList = TransportFactory.makeTransportEntityList(5, true) + robot.stubTransportMapperToModel(transportEntityList, transportList) + robot.stubDataStoreGetAllFavoritesSuccess(transportEntityList) + // Act + favoritesRepository.getAll() + // Assert + verify(mockTransportDataStore, times(1)).getAllFavorites() + } + + @Test + fun getAllFavoritesReturnsCorrectData() { + // Assemble + val transportList = TransportFactory.makeTransportList(5, true) + val transportEntityList = TransportFactory.makeTransportEntityList(5, true) + robot.stubTransportMapperToModel(transportEntityList, transportList) + robot.stubDataStoreGetAllFavoritesSuccess(transportEntityList) + // Act + val returnedData = favoritesRepository.getAll().test() + // Assert + returnedData.assertResult(transportList) + } + + @Test + fun getAllFavoritesFailsThrowsException() { + // Assemble + val transportList = TransportFactory.makeTransportList(5, true) + val transportEntityList = TransportFactory.makeTransportEntityList(5, true) + robot.stubTransportMapperToModel(transportEntityList, transportList) + + val errorMessage = "Test getAllFavorites fails" + robot.stubDataStoreGetAllFavoriteFails(errorMessage) + // Act + val testObserver = favoritesRepository.getAll().test() + // Assert + testObserver.assertFailureAndMessage(Throwable::class.java, errorMessage) + } + + // endregion getAllFavorites + + // region clearAll + + @Test + fun clearAllCompletes() { + // Assemble + robot.stubclearAllSuccess() + // Act + val testObserver = favoritesRepository.clearAll().test() + // Assert + testObserver.assertComplete() + } + + @Test + fun clearAllCallsCorrectTransportDataStoreMethod() { + // Assemble + robot.stubclearAllSuccess() + // Act + favoritesRepository.clearAll().test() + // Assert + verify(mockTransportDataStore, times(1)).clearAllFavorites() + } + + + @Test + fun clearAllFailsThrowsException() { + // Assemble + val errorMessage = "Testing clearAll failed" + robot.stubclearAllFails(errorMessage) + // Act + val testObserver = favoritesRepository.clearAll().test() + // Assert + testObserver.assertFailureAndMessage(Throwable::class.java, errorMessage) + } + + // endregion clearAll + + + inner class Robot { + + fun stubDataStoreMarkAsFavoriteSuccess(transportEntity: TransportEntity = TransportFactory.makeTransportEntity(false), completable: Completable = Completable.complete()): TransportEntity { + whenever(mockTransportDataStore.markAsFavorite(transportEntity)).then { completable } + return transportEntity + } + + fun stubDataStoreMarkAsFavoriteFails(message: String = randomUuid()): Throwable { + val throwable = Throwable(message) + whenever(mockTransportDataStore.markAsFavorite(anyOrNull())).then { Completable.error(throwable) } + return throwable + } + + fun stubDataStoreRemoveAsFavoriteSuccess(transportEntity: TransportEntity = TransportFactory.makeTransportEntity(true), completable: Completable = Completable.complete()): TransportEntity { + whenever(mockTransportDataStore.removeAsFavorite(transportEntity)).then { completable } + return transportEntity + } + + fun stubDataStoreRemoveAsFavoriteFails(message: String = randomUuid()): Throwable { + val throwable = Throwable(message) + whenever(mockTransportDataStore.removeAsFavorite(anyOrNull())).then { Completable.error(throwable) } + return throwable + } + + fun stubDataStoreGetAllFavoritesSuccess(transportEntityList: List = TransportFactory.makeTransportEntityList(5, true)): List { + whenever(mockTransportDataStore.getAllFavorites()).then { Flowable.just(transportEntityList) } + return transportEntityList + } + + fun stubDataStoreGetAllFavoriteFails(message: String = randomUuid()): Throwable { + val throwable = Throwable(message) + whenever(mockTransportDataStore.getAllFavorites()).then { Flowable.error(throwable) } + return throwable + } + + fun stubclearAllSuccess(completable: Completable = Completable.complete()) { + whenever(mockTransportDataStore.clearAllFavorites()).then { completable } + } + + fun stubclearAllFails(message: String = randomUuid()): Throwable { + val throwable = Throwable(message) + whenever(mockTransportDataStore.clearAllFavorites()).then { Completable.error(throwable) } + return throwable + } + + + fun stubTransportMapperToEntity(transport: List, transportEntity: List) { + whenever(mockMapper.toEntity(transport)).then { transportEntity } + } + + fun stubTransportMapperToEntity(transport: Transport, transportEntity: TransportEntity) { + whenever(mockMapper.toEntity(transport)).then { transportEntity } + } + + fun stubTransportMapperToModel(transportEntityList: List, transportList: List) { + whenever(mockMapper.toModel(transportEntityList)).then { transportList } + } + + fun stubTransportMapperToModel(transportEntity: TransportEntity, transport: Transport) { + whenever(mockMapper.toModel(transportEntity)).then { transport } + } + + + } +} \ No newline at end of file diff --git a/transport-eta-android/data/src/test/java/com/joaquimley/data/TransportRepositoryTest.kt b/transport-eta-android/data/src/test/java/com/joaquimley/data/TransportRepositoryTest.kt new file mode 100644 index 0000000..1e28719 --- /dev/null +++ b/transport-eta-android/data/src/test/java/com/joaquimley/data/TransportRepositoryTest.kt @@ -0,0 +1,44 @@ +package com.joaquimley.data + +import org.junit.After +import org.junit.Before +import org.junit.Test + +class TransportRepositoryTest { + + @Before + fun setUp() { + } + + @After + fun tearDown() { + } + + @Test + fun requestTransportEta() { + } + + @Test + fun cancelTransportEtaRequest() { + } + + @Test + fun getTransport() { + } + + @Test + fun saveTransport() { + } + + @Test + fun saveTransport1() { + } + + @Test + fun deleteTransport() { + } + + @Test + fun deleteTransports() { + } +} \ No newline at end of file diff --git a/transport-eta-android/data/src/test/java/com/joaquimley/data/factory/DataFactory.kt b/transport-eta-android/data/src/test/java/com/joaquimley/data/factory/DataFactory.kt new file mode 100644 index 0000000..b1e18f5 --- /dev/null +++ b/transport-eta-android/data/src/test/java/com/joaquimley/data/factory/DataFactory.kt @@ -0,0 +1,26 @@ +package com.joaquimley.data.factory + +import java.util.concurrent.ThreadLocalRandom + +/** + * Factory class for data instances + */ +class DataFactory { + + companion object Factory { + + fun randomInt(): Int { + return ThreadLocalRandom.current().nextInt(0, 1000 + 1) + } + + fun randomLong(): Long { + return randomInt().toLong() + } + + fun randomUuid(): String { + return java.util.UUID.randomUUID().toString() + } + + } + +} \ No newline at end of file diff --git a/transport-eta-android/data/src/test/java/com/joaquimley/data/factory/TransportFactory.kt b/transport-eta-android/data/src/test/java/com/joaquimley/data/factory/TransportFactory.kt new file mode 100644 index 0000000..5efd457 --- /dev/null +++ b/transport-eta-android/data/src/test/java/com/joaquimley/data/factory/TransportFactory.kt @@ -0,0 +1,41 @@ +package com.joaquimley.data.factory + +import com.joaquimley.data.factory.DataFactory.Factory.randomInt +import com.joaquimley.data.factory.DataFactory.Factory.randomUuid +import com.joaquimley.data.model.TransportEntity +import com.joaquimley.transporteta.domain.model.Transport + +/** + * Factory class for [Transport] related instances + */ +class TransportFactory { + + companion object Factory { + + fun makeTransportList(count: Int, isFavorite: Boolean = false, type: String = randomUuid()): List { + val transports = mutableListOf() + repeat(count) { + transports.add(makeTransport(isFavorite, type)) + } + return transports + } + + fun makeTransport(isFavorite: Boolean = false, type: String = randomUuid()): Transport { + return Transport(randomUuid(), randomUuid(), randomInt(), randomUuid(), isFavorite, type) + } + + fun makeTransportEntityList(count: Int, isFavorite: Boolean = false, type: String = randomUuid()): List { + val transports = mutableListOf() + repeat(count) { + transports.add(makeTransportEntity(isFavorite, type)) + } + return transports + } + + fun makeTransportEntity(isFavorite: Boolean = false, type: String = randomUuid()): TransportEntity { + return TransportEntity(randomUuid(), randomUuid(), randomInt(), randomUuid(), isFavorite, type) + } + + } + +} \ No newline at end of file diff --git a/transport-eta-android/data/src/test/java/com/joaquimley/data/mapper/TransportMapperTest.kt b/transport-eta-android/data/src/test/java/com/joaquimley/data/mapper/TransportMapperTest.kt new file mode 100644 index 0000000..8445e6f --- /dev/null +++ b/transport-eta-android/data/src/test/java/com/joaquimley/data/mapper/TransportMapperTest.kt @@ -0,0 +1,77 @@ +package com.joaquimley.data.mapper + +import com.joaquimley.data.factory.TransportFactory +import com.joaquimley.data.model.TransportEntity +import com.joaquimley.transporteta.domain.model.Transport +import org.junit.Before +import org.junit.Test + +class TransportMapperTest { + + private val robot = Robot() + private lateinit var mapper: TransportMapper + + @Before + fun setup() { + mapper = TransportMapper() + } + + @Test + fun fromModelToView() { + // Assemble + val stubbed = TransportFactory.makeTransport() + // Act + val mapped = mapper.toEntity(stubbed) + // Assert + assert(robot.areItemsTheSame(stubbed, mapped)) + } + + @Test + fun fromModelListToViewList() { + // Assemble + val stubbed = TransportFactory.makeTransportList(5) + // Act + val mapped = mapper.toEntity(stubbed) + // Assert + assert(robot.areItemsInListTheSame(stubbed, mapped)) + } + + @Test + fun fromViewToModel() { + // Assemble + val stubbed = TransportFactory.makeTransportEntity() + // Act + val mapped = mapper.toModel(stubbed) + // Assert + assert(robot.areItemsTheSame(mapped, stubbed)) + } + + @Test + fun fromViewListToModelList() { + // Assemble + val stubbed = TransportFactory.makeTransportEntityList(5) + // Act + val mapped = mapper.toModel(stubbed) + // Assert + assert(robot.areItemsInListTheSame(mapped, stubbed)) + } + + inner class Robot { + fun areItemsInListTheSame(transportList: List, transportViewList: List): Boolean { + for (transport in transportList.withIndex()) { + if (!areItemsTheSame(transportList[transport.index], transportViewList[transport.index])) { + return false + } + } + return true + } + + fun areItemsTheSame(transport: Transport, transportView: TransportEntity): Boolean { + return transport.id == transportView.id && + transport.code == transportView.code && + transport.latestEta == transportView.latestEta && + transport.isFavorite == transportView.isFavorite && + transport.type.equals(transportView.type) + } + } +} diff --git a/transport-eta-android/domain/build.gradle b/transport-eta-android/domain/build.gradle index baf3240..46cfe91 100644 --- a/transport-eta-android/domain/build.gradle +++ b/transport-eta-android/domain/build.gradle @@ -20,7 +20,6 @@ dependencies { * Testing * ***********/ - // Testing testImplementation deps.junit testImplementation deps.kotlin.test testImplementation deps.mockito.kotlin diff --git a/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/interactor/transport/favorites/ClearAllTransportsAsFavoriteUseCase.kt b/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/interactor/transport/favorites/ClearAllTransportsAsFavoriteUseCase.kt index f863605..b8219ff 100644 --- a/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/interactor/transport/favorites/ClearAllTransportsAsFavoriteUseCase.kt +++ b/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/interactor/transport/favorites/ClearAllTransportsAsFavoriteUseCase.kt @@ -15,6 +15,6 @@ class ClearAllTransportsAsFavoriteUseCase @Inject constructor(private val favori * Builds a [Completable] which will be used when the current [CompletableUseCase] is executed. */ public override fun buildUseCaseObservable(params: Void?): Completable { - return favoritesRepository.clearFavorites() + return favoritesRepository.clearAll() } } \ No newline at end of file diff --git a/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/repository/FavoritesRepository.kt b/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/repository/FavoritesRepository.kt index bb62d6d..399e2b3 100644 --- a/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/repository/FavoritesRepository.kt +++ b/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/repository/FavoritesRepository.kt @@ -11,11 +11,11 @@ import io.reactivex.Flowable */ interface FavoritesRepository { - fun markAsFavorite(favorite: Transport): Completable + fun markAsFavorite(transport: Transport): Completable fun removeAsFavorite(transport: Transport): Completable fun getAll(): Flowable> - fun clearFavorites(): Completable + fun clearAll(): Completable } \ No newline at end of file diff --git a/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/repository/MockFavoritesRepository.kt b/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/repository/MockFavoritesRepository.kt deleted file mode 100644 index 552d445..0000000 --- a/transport-eta-android/domain/src/main/java/com/joaquimley/transporteta/domain/repository/MockFavoritesRepository.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.joaquimley.transporteta.domain.repository - -import com.joaquimley.transporteta.domain.model.Transport -import io.reactivex.Completable -import io.reactivex.Flowable - -class MockFavoritesRepository: FavoritesRepository { - override fun markAsFavorite(favorite: Transport): Completable { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun removeAsFavorite(transport: Transport): Completable { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun getAll(): Flowable> { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - override fun clearFavorites(): Completable { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - -} \ No newline at end of file diff --git a/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/test/factory/DataFactory.kt b/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/test/factory/DataFactory.kt index 19e4693..65a2086 100644 --- a/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/test/factory/DataFactory.kt +++ b/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/test/factory/DataFactory.kt @@ -1,4 +1,4 @@ -package org.buffer.android.boilerplate.domain.test.factory +package com.joaquimley.transporteta.domain.test.factory import java.util.concurrent.ThreadLocalRandom diff --git a/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/test/factory/TransportFactory.kt b/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/test/factory/TransportFactory.kt index c5905b3..42bd1e7 100644 --- a/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/test/factory/TransportFactory.kt +++ b/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/test/factory/TransportFactory.kt @@ -1,8 +1,8 @@ package com.joaquimley.transporteta.domain.test.factory import com.joaquimley.transporteta.domain.model.Transport -import org.buffer.android.boilerplate.domain.test.factory.DataFactory.Factory.randomInt -import org.buffer.android.boilerplate.domain.test.factory.DataFactory.Factory.randomUuid +import com.joaquimley.transporteta.domain.test.factory.DataFactory.Factory.randomInt +import com.joaquimley.transporteta.domain.test.factory.DataFactory.Factory.randomUuid /** * Factory class for [Transport] related instances diff --git a/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/usecase/transport/favorites/ClearAllTransportsAsFavoriteUseCaseTest.kt b/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/usecase/transport/favorites/ClearAllTransportsAsFavoriteUseCaseTest.kt index 88c2dd0..d5b3de0 100644 --- a/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/usecase/transport/favorites/ClearAllTransportsAsFavoriteUseCaseTest.kt +++ b/transport-eta-android/domain/src/test/java/com/joaquimley/transporteta/domain/usecase/transport/favorites/ClearAllTransportsAsFavoriteUseCaseTest.kt @@ -40,12 +40,12 @@ class ClearAllTransportsAsFavoriteUseCaseTest { @Test fun buildUseCaseObservableCallsRepository() { clearAllTransportsAsFavoriteUseCase.buildUseCaseObservable(null) - verify(favoritesRepository).clearFavorites() + verify(favoritesRepository).clearAll() } inner class Robot { fun stubTransportRepositoryClearFavorites(completable: Completable) { - whenever(favoritesRepository.clearFavorites()).thenReturn(completable) + whenever(favoritesRepository.clearAll()).thenReturn(completable) } } diff --git a/transport-eta-android/settings.gradle b/transport-eta-android/settings.gradle index cd2f4d9..de9fbec 100644 --- a/transport-eta-android/settings.gradle +++ b/transport-eta-android/settings.gradle @@ -1 +1 @@ -include ':ui-mobile', ':sms', ':presentation', ':domain' +include ':ui-mobile', ':sms', ':presentation', ':domain', ':data' diff --git a/transport-eta-android/ui-mobile/build.gradle b/transport-eta-android/ui-mobile/build.gradle index 9ec2d2f..1f05db7 100644 --- a/transport-eta-android/ui-mobile/build.gradle +++ b/transport-eta-android/ui-mobile/build.gradle @@ -80,8 +80,9 @@ androidExtensions { dependencies { // Modules - implementation project(':domain') // REMOVE AFTER HAVING :data layer implementation project(':sms') + implementation project(':data') + implementation project(':domain') implementation project(':presentation') // AndroidX implementation deps.androidx.slice diff --git a/transport-eta-android/ui-mobile/src/debug/java/com/joaquimley/transporteta/ui/testing/factory/TestModelsFactory.kt b/transport-eta-android/ui-mobile/src/debug/java/com/joaquimley/transporteta/ui/testing/factory/TestModelsFactory.kt index 1a09643..2103516 100644 --- a/transport-eta-android/ui-mobile/src/debug/java/com/joaquimley/transporteta/ui/testing/factory/TestModelsFactory.kt +++ b/transport-eta-android/ui-mobile/src/debug/java/com/joaquimley/transporteta/ui/testing/factory/TestModelsFactory.kt @@ -8,7 +8,7 @@ object TestModelsFactory { fun generateFavoriteView(busStopCode: Int? = null): TransportView { return TransportView(DataFactory.randomString(), busStopCode ?: DataFactory.randomInt(), - DataFactory.randomString(), true, TransportView.Type.BUS, true) + DataFactory.randomString(), true, TransportView.Type.BUS) } fun generateFavoriteViewList(size: Int = 5): List { diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/component/AppComponent.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/component/AppComponent.kt index 73f90c5..59c48dd 100644 --- a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/component/AppComponent.kt +++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/component/AppComponent.kt @@ -13,11 +13,14 @@ import dagger.android.support.AndroidSupportInjectionModule AppModule::class, ActivityBindingModule::class, AndroidSupportInjectionModule::class, + MapperModule::class, ViewModelModule::class, RepositoryModule::class, + DataSourceModule::class, SmsControllerModule::class ]) interface AppComponent { + @Component.Builder interface Builder { @BindsInstance diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/AppModule.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/AppModule.kt index cff2331..c7f6935 100644 --- a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/AppModule.kt +++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/AppModule.kt @@ -2,6 +2,7 @@ package com.joaquimley.transporteta.ui.di.module import android.app.Application import android.content.Context +import com.joaquimley.data.executor.ThreadExecutorImpl import com.joaquimley.transporteta.domain.executor.PostExecutionThread import com.joaquimley.transporteta.domain.executor.ThreadExecutor import com.joaquimley.transporteta.ui.UiThread @@ -9,7 +10,6 @@ import com.joaquimley.transporteta.ui.di.component.SmsControllerSubComponent import com.joaquimley.transporteta.ui.di.scope.PerApplication import dagger.Module import dagger.Provides -import io.reactivex.schedulers.Schedulers /** * Module used to provide application-level dependencies. @@ -21,8 +21,8 @@ class AppModule { @Provides @PerApplication - internal fun provideThreadExecutor(executor: Schedulers): ThreadExecutor { - return executor as ThreadExecutor + internal fun provideThreadExecutor(): ThreadExecutor { + return ThreadExecutorImpl() } @Provides diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/DataSourceModule.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/DataSourceModule.kt new file mode 100644 index 0000000..d38f8b0 --- /dev/null +++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/DataSourceModule.kt @@ -0,0 +1,27 @@ +package com.joaquimley.transporteta.ui.di.module + +import dagger.Module + +@Module +class DataSourceModule { + +// @Provides +// @PerApplication +// fun provideSharedPreferencesDataSource(): SharedPreferencesDataSource { +// return +// } + +// @Provides +// @PerApplication +// fun provideCacheDataSource(): CacheDataSource { +// return +// } + +// @Provides +// @PerApplication +// fun provideRemoteDataSource(): TransportRepository { +// return TransportRepositoryImpl() +// } + + +} \ No newline at end of file diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/MapperModule.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/MapperModule.kt new file mode 100644 index 0000000..033a428 --- /dev/null +++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/MapperModule.kt @@ -0,0 +1,15 @@ +package com.joaquimley.transporteta.ui.di.module + +import com.joaquimley.transporteta.presentation.mapper.TransportMapper +import dagger.Module +import dagger.Provides + +@Module +class MapperModule { + + @Provides + fun transportMapper(): TransportMapper { + return TransportMapper() + } + +} \ No newline at end of file diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/RepositoryModule.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/RepositoryModule.kt index 70a1288..62dce93 100644 --- a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/RepositoryModule.kt +++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/di/module/RepositoryModule.kt @@ -1,7 +1,10 @@ package com.joaquimley.transporteta.ui.di.module import com.joaquimley.transporteta.domain.repository.FavoritesRepository -import com.joaquimley.transporteta.domain.repository.MockFavoritesRepository +import com.joaquimley.data.FavoritesRepositoryImpl +import com.joaquimley.transporteta.domain.repository.TransportRepository +import com.joaquimley.data.TransportRepositoryImpl +import com.joaquimley.transporteta.ui.di.scope.PerApplication import dagger.Module import dagger.Provides @@ -9,8 +12,15 @@ import dagger.Provides class RepositoryModule { @Provides + @PerApplication fun provideFavoritesRepository(): FavoritesRepository { - return MockFavoritesRepository() + return FavoritesRepositoryImpl() + } + + @Provides + @PerApplication + fun provideTransportRepository(): TransportRepository { + return TransportRepositoryImpl() } diff --git a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/home/favorite/FavoritesAdapter.kt b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/home/favorite/FavoritesAdapter.kt index e267cf8..cd8974e 100644 --- a/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/home/favorite/FavoritesAdapter.kt +++ b/transport-eta-android/ui-mobile/src/main/java/com/joaquimley/transporteta/ui/home/favorite/FavoritesAdapter.kt @@ -40,9 +40,9 @@ class FavoritesAdapter(private val clickListener: (TransportView) -> Unit) } fun setActionEnabledStatus(isEnabled: Boolean) { - for (position in 0 until itemCount) { - getItem(position).isActionEnabled = isEnabled - } +// for (position in 0 until itemCount) { +// getItem(position).isActionEnabled = isEnabled +// } notifyDataSetChanged() } @@ -55,10 +55,10 @@ class FavoritesAdapter(private val clickListener: (TransportView) -> Unit) itemView.title_text_view.text = transportView.code.toString() itemView.subtitle_text_view.text = transportView.latestEta itemView.original_sms_text_view.text = transportView.latestEta - transportView.isActionEnabled.let { - itemView.eta_button.isEnabled = it - itemView.eta_button.alpha = if (it) 1.0f else 0.3f - } +// transportView.isActionEnabled.let { +// itemView.eta_button.isEnabled = it +// itemView.eta_button.alpha = if (it) 1.0f else 0.3f +// } itemView.eta_button.setOnClickListener { clickListener(transportView)