diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..591f8b0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: gradle + directory: "/" + target-branch: "develop" + schedule: + interval: "weekly" + day: "monday" + time: "12:00" + open-pull-requests-limit: 15 \ No newline at end of file diff --git a/.github/workflows/pull-request-jobs.yml b/.github/workflows/pull-request-jobs.yml new file mode 100644 index 0000000..7aa4d80 --- /dev/null +++ b/.github/workflows/pull-request-jobs.yml @@ -0,0 +1,71 @@ +name: Verify Pull request is publishable + +on: + pull_request: + branches: + - develop + +env: + GITHUB_USERNAME: "fknives" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + run-code-analysis: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + - name: Run clean + run: ./gradlew clean + - name: Run detekt + run: ./gradlew detekt + - name: Upload Detekt Results + - uses: actions/upload-artifact@v2 + with: + name: Detekt Results + path: build/reports/detekt/detekt.html + retention-days: 1 + - name: Run ktlint + run: ./gradlew ktlintCheck + - name: Upload ktLint Results + - uses: actions/upload-artifact@v2 + with: + name: ktLint Results + path: ./**/build/reports/ktlint/**/*ktlint*Check.txt + retention-days: 1 + - name: Run Lint + run: ./gradlew lint + - name: Upload Lint Results + - uses: actions/upload-artifact@v2 + with: + name: Lint Results + path: ./**/build/reports/*lint-results*.html + retention-days: 1 + + run-tests: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + - name: Run Unit Tests + run: ./gradlew testReleaseUnit + - name: Upload Test Results + - uses: actions/upload-artifact@v2 + with: + name: Unit Test Results + path: ./**/build/reports/tests/**/index.html + retention-days: 1 diff --git a/app/build.gradle b/app/build.gradle index d63bebe..b227cb4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,6 @@ plugins { android { compileSdkVersion 30 - buildToolsVersion "30.0.3" defaultConfig { applicationId "org.fnives.test.showcase" @@ -25,16 +24,10 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } + buildFeatures { viewBinding true } - kotlinOptions { - jvmTarget = '1.8' - } sourceSets { androidTest { @@ -46,19 +39,6 @@ android { } } - testOptions.unitTests.all { - useJUnitPlatform() - testLogging { - events 'started', 'passed', 'skipped', 'failed' - exceptionFormat "full" - showStandardStreams true - } - } - testOptions { - unitTests { - includeAndroidResources = true - } - } // needed for androidTest packagingOptions { exclude 'META-INF/LGPL2.1' @@ -66,14 +46,6 @@ android { exclude 'META-INF/LICENSE.md' exclude 'META-INF/LICENSE-notice.md' } - - lintOptions { - warningsAsErrors true - abortOnError true - textReport true - ignore 'Overdraw' - textOutput "stdout" - } } afterEvaluate { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1000bd1..09b554f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,8 @@ android:name=".TestShowcaseApplication" android:theme="@style/Theme.TestShowCase" tools:ignore="AllowBackup"> - + diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/favourite/FavouriteContentLocalStorageImplTest.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/favourite/FavouriteContentLocalStorageImplTest.kt index 06d314b..4434ed6 100644 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/favourite/FavouriteContentLocalStorageImplTest.kt +++ b/app/src/robolectricTest/java/org/fnives/test/showcase/favourite/FavouriteContentLocalStorageImplTest.kt @@ -3,14 +3,11 @@ package org.fnives.test.showcase.favourite import androidx.test.ext.junit.runners.AndroidJUnit4 import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.take import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestCoroutineDispatcher -import kotlinx.coroutines.test.TestCoroutineScope -import kotlinx.coroutines.test.runBlockingTest import org.fnives.test.showcase.core.storage.content.FavouriteContentLocalStorage import org.fnives.test.showcase.model.content.ContentId import org.fnives.test.showcase.storage.database.DatabaseInitialization @@ -44,25 +41,26 @@ internal class FavouriteContentLocalStorageImplTest : KoinTest { @Test fun GIVEN_content_id_WHEN_added_to_Favourite_THEN_it_can_be_read_out() = runBlocking { - val expected = listOf(ContentId("a")) + val expected = listOf(ContentId("a")) - sut.markAsFavourite(ContentId("a")) - val actual = sut.observeFavourites().first() - - Assert.assertEquals(expected, actual) - } - - @Test - fun GIVEN_content_id_added_WHEN_removed_to_Favourite_THEN_it_no_longer_can_be_read_out() = runBlocking { - val expected = listOf() - sut.markAsFavourite(ContentId("b")) - - sut.deleteAsFavourite(ContentId("b")) + sut.markAsFavourite(ContentId("a")) val actual = sut.observeFavourites().first() Assert.assertEquals(expected, actual) } + @Test + fun GIVEN_content_id_added_WHEN_removed_to_Favourite_THEN_it_no_longer_can_be_read_out() = + runBlocking { + val expected = listOf() + sut.markAsFavourite(ContentId("b")) + + sut.deleteAsFavourite(ContentId("b")) + val actual = sut.observeFavourites().first() + + Assert.assertEquals(expected, actual) + } + @Test fun GIVEN_empty_database_WHILE_observing_content_WHEN_favourite_added_THEN_change_is_emitted() = runBlocking { diff --git a/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt b/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt index 917f67e..1d01ef7 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt @@ -27,24 +27,20 @@ class CodeKataAuthViewModel { @DisplayName("GIVEN_initialized_viewModel_WHEN_observed_THEN_loading_false_other_fields_are_empty") @Test fun initialSetup() { - } @DisplayName("GIVEN_password_text_WHEN_onPasswordChanged_is_called_THEN_password_livedata_is_updated") @Test fun whenPasswordChangedLiveDataIsUpdated() { - } @DisplayName("GIVEN_username_text_WHEN_onUsernameChanged_is_called_THEN_username_livedata_is_updated") @Test fun whenUsernameChangedLiveDataIsUpdated() { - } @DisplayName("GIVEN_no_password_or_username_WHEN_login_is_Called_THEN_empty_credentials_are_used_in_usecase") @Test fun noPasswordUsesEmptyStringInLoginUseCase() { - } -} \ No newline at end of file +} diff --git a/app/src/test/java/org/fnives/test/showcase/ui/splash/CodeKataSplashViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/splash/CodeKataSplashViewModelTest.kt index 4e6738d..475bb22 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/splash/CodeKataSplashViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/splash/CodeKataSplashViewModelTest.kt @@ -1,41 +1,29 @@ package org.fnives.test.showcase.ui.splash -import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase -import org.fnives.test.showcase.testutils.InstantExecutorExtension -import org.fnives.test.showcase.testutils.TestMainDispatcher -import org.fnives.test.showcase.ui.shared.Event import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever @Disabled("CodeKata") internal class CodeKataSplashViewModelTest { @BeforeEach fun setUp() { - } @DisplayName("GIVEN not logged in user WHEN splash started THEN after half a second navigated to authentication") @Test fun loggedOutUserGoesToAuthentication() { - } @DisplayName("GIVEN logged in user WHEN splash started THEN after half a second navigated to home") @Test fun loggedInUserGoestoHome() { - } @DisplayName("GIVEN not logged in user WHEN splash started THEN before half a second no event is sent") @Test fun withoutEnoughTimeNoNavigationHappens() { - } -} \ No newline at end of file +} diff --git a/build.gradle b/build.gradle index a53abdc..c09cd78 100644 --- a/build.gradle +++ b/build.gradle @@ -1,49 +1,22 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = "1.5.30" - ext.detekt_version = "1.16.0" + ext.detekt_version = "1.18.1" repositories { mavenCentral() google() maven { url "https://plugins.gradle.org/m2/" } } dependencies { - classpath "com.android.tools.build:gradle:4.1.3" + classpath 'com.android.tools.build:gradle:7.0.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "org.jlleitschuh.gradle:ktlint-gradle:10.0.0" - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.0" } } -//apply plugin: "io.gitlab.arturbosch.detekt" version "$detekt_version" plugins { id "io.gitlab.arturbosch.detekt" version "$detekt_version" } -detekt { - toolVersion = "$detekt_version" - - input = files( - "$projectDir/app/src/main/java", - "$projectDir/core/src/main/java", - "$projectDir/mockserver/src/main/java", - "$projectDir/model/src/main/java", - "$projectDir/network/src/main/java" - ) - config = files("$projectDir/detekt/detekt.yml") - baseline = file("$projectDir/detekt/baseline.xml") - reports { - txt { - enabled = true - destination = file("build/reports/detekt.txt") - } - html { - enabled = true - destination = file("build/reports/detekt.html") - } - } -} allprojects { repositories { @@ -52,10 +25,6 @@ allprojects { } } -subprojects { - apply plugin: "org.jlleitschuh.gradle.ktlint" -} - task clean(type: Delete) { delete rootProject.buildDir } @@ -70,4 +39,8 @@ task androidTests(dependsOn: "app:connectedAndroidTest"){ description = 'Run all Android tests' } -apply from: 'gradlescripts/versions.gradle' \ No newline at end of file +apply from: 'gradlescripts/versions.gradle' +apply from: 'gradlescripts/detekt.config.gradle' +apply from: 'gradlescripts/ktlint.gradle' +apply from: 'gradlescripts/lint.gradle' +apply from: 'gradlescripts/testoptions.gradle' \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle index 5d442cc..745175d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -8,15 +8,6 @@ java { targetCompatibility = JavaVersion.VERSION_1_8 } -test { - useJUnitPlatform() - testLogging { - events 'started', 'passed', 'skipped', 'failed' - exceptionFormat "full" - showStandardStreams true - } -} - compileKotlin { kotlinOptions { freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" diff --git a/core/src/test/java/org/fnives/test/showcase/core/content/CodeKataContentRepositoryTest.kt b/core/src/test/java/org/fnives/test/showcase/core/content/CodeKataContentRepositoryTest.kt index c8f74fe..fb70a9a 100644 --- a/core/src/test/java/org/fnives/test/showcase/core/content/CodeKataContentRepositoryTest.kt +++ b/core/src/test/java/org/fnives/test/showcase/core/content/CodeKataContentRepositoryTest.kt @@ -1,75 +1,50 @@ package org.fnives.test.showcase.core.content -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.async -import kotlinx.coroutines.flow.take -import kotlinx.coroutines.flow.toList -import kotlinx.coroutines.test.TestCoroutineDispatcher import kotlinx.coroutines.test.runBlockingTest -import org.fnives.test.showcase.core.shared.UnexpectedException -import org.fnives.test.showcase.model.content.Content -import org.fnives.test.showcase.model.content.ContentId -import org.fnives.test.showcase.model.content.ImageUrl -import org.fnives.test.showcase.model.shared.Resource -import org.fnives.test.showcase.network.content.ContentRemoteSource -import org.junit.jupiter.api.* -import org.mockito.kotlin.doAnswer -import org.mockito.kotlin.doReturn -import org.mockito.kotlin.doSuspendableAnswer -import org.mockito.kotlin.doThrow -import org.mockito.kotlin.mock -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions -import org.mockito.kotlin.whenever +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test @Disabled("CodeKata") class CodeKataContentRepositoryTest { @BeforeEach fun setUp() { - } @DisplayName("GIVEN no interaction THEN remote source is not called") @Test fun fetchingIsLazy() { - } @DisplayName("GIVEN content response WHEN content observed THEN loading AND data is returned") @Test fun happyFlow() = runBlockingTest { - } @DisplayName("GIVEN content error WHEN content observed THEN loading AND data is returned") @Test fun errorFlow() = runBlockingTest { - } @DisplayName("GIVEN saved cache WHEN collected THEN cache is returned") @Test fun verifyCaching() = runBlockingTest { - } @DisplayName("GIVEN no response from remote source WHEN content observed THEN loading is returned") @Test fun loadingIsShownBeforeTheRequestIsReturned() = runBlockingTest { - } @DisplayName("GIVEN content response THEN error WHEN fetched THEN returned states are loading data loading error") @Test fun whenFetchingRequestIsCalledAgain() = runBlockingTest { - } @DisplayName("GIVEN content response THEN error WHEN fetched THEN only 4 items are emitted") @Test fun noAdditionalItemsEmitted() { - } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/fnives/test/showcase/core/content/ContentRepositoryTest.kt b/core/src/test/java/org/fnives/test/showcase/core/content/ContentRepositoryTest.kt index 7398fd4..eaeb901 100644 --- a/core/src/test/java/org/fnives/test/showcase/core/content/ContentRepositoryTest.kt +++ b/core/src/test/java/org/fnives/test/showcase/core/content/ContentRepositoryTest.kt @@ -16,7 +16,15 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import org.mockito.kotlin.* +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doSuspendableAnswer +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever @Suppress("TestFunctionName") internal class ContentRepositoryTest { @@ -43,10 +51,19 @@ internal class ContentRepositoryTest { @Test fun happyFlow() = runBlockingTest { val expected = listOf( - Resource.Loading(), - Resource.Success(listOf(Content(ContentId("a"), "", "", ImageUrl("")))) + Resource.Loading(), + Resource.Success(listOf(Content(ContentId("a"), "", "", ImageUrl("")))) + ) + whenever(mockContentRemoteSource.get()).doReturn( + listOf( + Content( + ContentId("a"), + "", + "", + ImageUrl("") + ) + ) ) - whenever(mockContentRemoteSource.get()).doReturn(listOf(Content(ContentId("a"), "", "", ImageUrl("")))) val actual = sut.contents.take(2).toList() @@ -58,8 +75,8 @@ internal class ContentRepositoryTest { fun errorFlow() = runBlockingTest { val exception = RuntimeException() val expected = listOf( - Resource.Loading(), - Resource.Error>(UnexpectedException(exception)) + Resource.Loading(), + Resource.Error>(UnexpectedException(exception)) ) whenever(mockContentRemoteSource.get()).doThrow(exception) @@ -101,27 +118,27 @@ internal class ContentRepositoryTest { @DisplayName("GIVEN content response THEN error WHEN fetched THEN returned states are loading data loading error") @Test fun whenFetchingRequestIsCalledAgain() = - runBlockingTest(testDispatcher) { - val exception = RuntimeException() - val expected = listOf( - Resource.Loading(), - Resource.Success(emptyList()), - Resource.Loading(), - Resource.Error>(UnexpectedException(exception)) - ) - var first = true - whenever(mockContentRemoteSource.get()).doAnswer { - if (first) emptyList().also { first = false } else throw exception - } - - val actual = async(testDispatcher) { sut.contents.take(4).toList() } - testDispatcher.advanceUntilIdle() - sut.fetch() - testDispatcher.advanceUntilIdle() - - Assertions.assertEquals(expected, actual.await()) + runBlockingTest(testDispatcher) { + val exception = RuntimeException() + val expected = listOf( + Resource.Loading(), + Resource.Success(emptyList()), + Resource.Loading(), + Resource.Error>(UnexpectedException(exception)) + ) + var first = true + whenever(mockContentRemoteSource.get()).doAnswer { + if (first) emptyList().also { first = false } else throw exception } + val actual = async(testDispatcher) { sut.contents.take(4).toList() } + testDispatcher.advanceUntilIdle() + sut.fetch() + testDispatcher.advanceUntilIdle() + + Assertions.assertEquals(expected, actual.await()) + } + @DisplayName("GIVEN content response THEN error WHEN fetched THEN only 4 items are emitted") @Test fun noAdditionalItemsEmitted() { @@ -129,10 +146,10 @@ internal class ContentRepositoryTest { runBlockingTest(testDispatcher) { val exception = RuntimeException() val expected = listOf( - Resource.Loading(), - Resource.Success(emptyList()), - Resource.Loading(), - Resource.Error>(UnexpectedException(exception)) + Resource.Loading(), + Resource.Success(emptyList()), + Resource.Loading(), + Resource.Error>(UnexpectedException(exception)) ) var first = true whenever(mockContentRemoteSource.get()).doAnswer { diff --git a/core/src/test/java/org/fnives/test/showcase/core/login/CodeKataSecondLoginUseCaseTest.kt b/core/src/test/java/org/fnives/test/showcase/core/login/CodeKataSecondLoginUseCaseTest.kt index 3bef542..a689882 100644 --- a/core/src/test/java/org/fnives/test/showcase/core/login/CodeKataSecondLoginUseCaseTest.kt +++ b/core/src/test/java/org/fnives/test/showcase/core/login/CodeKataSecondLoginUseCaseTest.kt @@ -1,16 +1,9 @@ package org.fnives.test.showcase.core.login -import kotlinx.coroutines.test.runBlockingTest -import org.fnives.test.showcase.core.shared.UnexpectedException -import org.fnives.test.showcase.core.storage.UserDataLocalStorage -import org.fnives.test.showcase.model.auth.LoginCredentials -import org.fnives.test.showcase.model.auth.LoginStatus -import org.fnives.test.showcase.model.session.Session -import org.fnives.test.showcase.model.shared.Answer -import org.fnives.test.showcase.network.auth.LoginRemoteSource -import org.fnives.test.showcase.network.auth.model.LoginStatusResponses -import org.junit.jupiter.api.* -import org.mockito.kotlin.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test @Disabled("CodeKata") class CodeKataSecondLoginUseCaseTest { @@ -49,4 +42,4 @@ class CodeKataSecondLoginUseCaseTest { fun invalidResponseResultsInErrorReturned() { TODO() } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/fnives/test/showcase/core/login/LoginUseCaseTest.kt b/core/src/test/java/org/fnives/test/showcase/core/login/LoginUseCaseTest.kt index b5050ac..142593f 100644 --- a/core/src/test/java/org/fnives/test/showcase/core/login/LoginUseCaseTest.kt +++ b/core/src/test/java/org/fnives/test/showcase/core/login/LoginUseCaseTest.kt @@ -13,8 +13,14 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test -import org.mockito.kotlin.* -import java.util.stream.Stream +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyZeroInteractions +import org.mockito.kotlin.whenever @Suppress("TestFunctionName") internal class LoginUseCaseTest { diff --git a/core/src/test/java/org/fnives/test/showcase/core/session/CodeKataFirstSessionExpirationAdapterTest.kt b/core/src/test/java/org/fnives/test/showcase/core/session/CodeKataFirstSessionExpirationAdapterTest.kt index 686fa7f..6454d59 100644 --- a/core/src/test/java/org/fnives/test/showcase/core/session/CodeKataFirstSessionExpirationAdapterTest.kt +++ b/core/src/test/java/org/fnives/test/showcase/core/session/CodeKataFirstSessionExpirationAdapterTest.kt @@ -1,12 +1,6 @@ package org.fnives.test.showcase.core.session -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.mockito.kotlin.* @Disabled("CodeKata") -class CodeKataFirstSessionExpirationAdapterTest { - -} \ No newline at end of file +class CodeKataFirstSessionExpirationAdapterTest diff --git a/detekt/detekt.yml b/detekt/detekt.yml index 3e05232..0fd7e71 100644 --- a/detekt/detekt.yml +++ b/detekt/detekt.yml @@ -1,56 +1,79 @@ build: - maxIssues: 15 + maxIssues: 0 + excludeCorrectable: false weights: - # complexity: 2 - # LongParameterList: 1 - # style: 1 - # comments: 1 + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 config: validation: true - # when writing own rules with new properties, exclude the property path e.g.: "my_rule_set,.*>.*>[my_property]" - excludes: "" + warningsAsErrors: false + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' processors: active: true exclude: - # - 'DetektProgressListener' + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' # - 'FunctionCountProcessor' # - 'PropertyCountProcessor' - # - 'ClassCountProcessor' - # - 'PackageCountProcessor' - # - 'KtFileCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' console-reports: active: true exclude: - # - 'ProjectStatisticsReport' - # - 'ComplexityReport' - # - 'NotificationReport' - # - 'FindingsReport' - - 'FileBasedFindingsReport' - # - 'BuildFailureReport' + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + # - 'FindingsReport' + - 'FileBasedFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' comments: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: false CommentOverPrivateFunction: active: false CommentOverPrivateProperty: active: false + DeprecatedBlockTag: + active: false EndOfSentenceFormat: active: false - endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!:]$) + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' UndocumentedPublicClass: active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] searchInNestedClass: true searchInInnerClass: true searchInInnerObject: true searchInInnerInterface: true UndocumentedPublicFunction: active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] UndocumentedPublicProperty: active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] complexity: active: true @@ -61,57 +84,86 @@ complexity: active: false threshold: 10 includeStaticDeclarations: false + includePrivateDeclarations: false ComplexMethod: active: true - threshold: 25 + threshold: 15 ignoreSingleWhenExpression: false ignoreSimpleWhenEntries: false ignoreNestingFunctions: false - nestingFunctions: run,let,apply,with,also,use,forEach,isNotNull,ifNull + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' LabeledExpression: active: false - ignoredLabels: "" + ignoredLabels: [] LargeClass: active: true threshold: 600 LongMethod: active: true - threshold: 200 + threshold: 60 + ignoreAnnotated: [] LongParameterList: active: true functionThreshold: 6 - constructorThreshold: 6 + constructorThreshold: 7 ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotated: [] MethodOverloading: active: false threshold: 6 + NamedArguments: + active: false + threshold: 3 NestedBlockDepth: active: true - threshold: 100 + threshold: 4 + ReplaceSafeCallChainWithRun: + active: false StringLiteralDuplication: active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] threshold: 3 ignoreAnnotation: true excludeStringsWithLessThan5Characters: true ignoreStringsRegex: '$^' TooManyFunctions: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - thresholdInFiles: 25 - thresholdInClasses: 25 - thresholdInInterfaces: 25 - thresholdInObjects: 25 - thresholdInEnums: 25 + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 ignoreDeprecated: false ignorePrivate: false ignoreOverridden: false +coroutines: + active: true + GlobalCoroutineUsage: + active: false + RedundantSuspendModifier: + active: false + SleepInsteadOfDelay: + active: false + SuspendFunWithFlowReturnType: + active: false + empty-blocks: active: true EmptyCatchBlock: active: true - allowedExceptionNameRegex: "^(_|(ignore|expected).*)" + allowedExceptionNameRegex: '_|(ignore|expected).*' EmptyClassBlock: active: true EmptyDefaultConstructor: @@ -135,6 +187,8 @@ empty-blocks: active: true EmptySecondaryConstructor: active: true + EmptyTryBlock: + active: true EmptyWhenBlock: active: true EmptyWhileBlock: @@ -143,53 +197,73 @@ empty-blocks: exceptions: active: true ExceptionRaisedInUnexpectedLocation: - active: false - methodNames: 'toString,hashCode,equals,finalize' + active: true + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' InstanceOfCheckForException: active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] NotImplementedDeclaration: active: false + ObjectExtendsThrowable: + active: false PrintStackTrace: - active: false + active: true RethrowCaughtException: - active: false + active: true ReturnFromFinally: - active: false + active: true ignoreLabeled: false SwallowedException: - active: false - ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException' - allowedExceptionNameRegex: "^(_|(ignore|expected).*)" + active: true + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' ThrowingExceptionFromFinally: - active: false + active: true ThrowingExceptionInMain: active: false ThrowingExceptionsWithoutMessageOrCause: active: true - exceptions: 'IllegalArgumentException,IllegalStateException,IOException' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' ThrowingNewInstanceOfSameException: active: true TooGenericExceptionCaught: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] exceptionNames: - - ArrayIndexOutOfBoundsException - - Error - - Exception - - IllegalMonitorStateException - - NullPointerException - - IndexOutOfBoundsException - - RuntimeException - - Throwable - allowedExceptionNameRegex: "^(_|(ignore|expected).*)" + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' TooGenericExceptionThrown: active: true exceptionNames: - - Error - - Exception - - Throwable - - RuntimeException + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' formatting: active: true @@ -198,28 +272,42 @@ formatting: AnnotationOnSeparateLine: active: false autoCorrect: true + AnnotationSpacing: + active: false + autoCorrect: true + ArgumentListWrapping: + active: false + autoCorrect: true + indentSize: 4 + maxLineLength: 120 ChainWrapping: active: true autoCorrect: true CommentSpacing: active: true autoCorrect: true + EnumEntryNameCase: + active: false + autoCorrect: true Filename: active: true FinalNewline: active: true autoCorrect: true + insertFinalNewLine: true ImportOrdering: - active: true + active: false autoCorrect: true + layout: '*,java.**,javax.**,kotlin.**,^' Indentation: - active: true + active: false autoCorrect: true indentSize: 4 continuationIndentSize: 4 MaximumLineLength: active: true - maxLineLength: 250 + maxLineLength: 120 + ignoreBackTickedIdentifier: false ModifierOrdering: active: true autoCorrect: true @@ -235,6 +323,9 @@ formatting: NoEmptyClassBody: active: true autoCorrect: true + NoEmptyFirstLineInMethodBlock: + active: false + autoCorrect: true NoLineBreakAfterElse: active: true autoCorrect: true @@ -265,6 +356,10 @@ formatting: active: true autoCorrect: true indentSize: 4 + maxLineLength: 120 + SpacingAroundAngleBrackets: + active: false + autoCorrect: true SpacingAroundColon: active: true autoCorrect: true @@ -277,6 +372,9 @@ formatting: SpacingAroundDot: active: true autoCorrect: true + SpacingAroundDoubleColon: + active: false + autoCorrect: true SpacingAroundKeyword: active: true autoCorrect: true @@ -289,85 +387,108 @@ formatting: SpacingAroundRangeOperator: active: true autoCorrect: true + SpacingAroundUnaryOperator: + active: false + autoCorrect: true + SpacingBetweenDeclarationsWithAnnotations: + active: false + autoCorrect: true + SpacingBetweenDeclarationsWithComments: + active: false + autoCorrect: true StringTemplate: active: true autoCorrect: true naming: active: true + BooleanPropertyNaming: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + allowedPattern: '^(is|has|are)' ClassNaming: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - classPattern: '[A-Z$][a-zA-Z0-9$]*' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + classPattern: '[A-Z][a-zA-Z0-9]*' ConstructorParameterNaming: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] parameterPattern: '[a-z][A-Za-z0-9]*' privateParameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' + ignoreOverridden: true EnumNaming: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' ForbiddenClassName: active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - forbiddenName: '' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + forbiddenName: [] FunctionMaxLength: active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] maximumFunctionNameLength: 30 FunctionMinLength: active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] minimumFunctionNameLength: 3 FunctionNaming: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' excludeClassPattern: '$^' ignoreOverridden: true + ignoreAnnotated: + - 'Composable' FunctionParameterNaming: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] parameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' ignoreOverridden: true InvalidPackageDeclaration: active: false + excludes: ['**/*.kts'] rootPackage: '' MatchingDeclarationName: active: true + mustBeFirst: true MemberNameEqualsClassName: active: true ignoreOverridden: true + NoNameShadowing: + active: false + NonBooleanPropertyPrefixedWithIs: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] ObjectPropertyNaming: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] constantPattern: '[A-Za-z][_A-Za-z0-9]*' propertyPattern: '[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' PackageNaming: - active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$' + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' TopLevelPropertyNaming: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] constantPattern: '[A-Z][_A-Z0-9]*' propertyPattern: '[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' VariableMaxLength: active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] maximumVariableNameLength: 64 VariableMinLength: active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] minimumVariableNameLength: 1 VariableNaming: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] variablePattern: '[a-z][A-Za-z0-9]*' privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' @@ -379,29 +500,52 @@ performance: active: true ForEachOnRange: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] SpreadOperator: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] UnnecessaryTemporaryInstantiation: active: true potential-bugs: active: true + AvoidReferentialEquality: + active: false + forbiddenTypePatterns: + - 'kotlin.String' + CastToNullableType: + active: false Deprecation: active: false + DontDowncastCollectionTypes: + active: false + DoubleMutabilityForCollection: + active: false DuplicateCaseInWhenExpression: active: true EqualsAlwaysReturnsTrueOrFalse: active: true EqualsWithHashCodeExist: active: true + ExitOutsideMain: + active: false ExplicitGarbageCollectionCall: active: true HasPlatformType: active: false - ImplicitDefaultLocale: + IgnoredReturnValue: active: false + restrictToAnnotatedMethods: true + returnValueAnnotations: + - '*.CheckResult' + - '*.CheckReturnValue' + ignoreReturnValueAnnotations: + - '*.CanIgnoreReturnValue' + ImplicitDefaultLocale: + active: true + ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true InvalidRange: active: true IteratorHasNextCallsNextMethod: @@ -410,20 +554,33 @@ potential-bugs: active: true LateinitUsage: active: false - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - excludeAnnotatedProperties: "" - ignoreOnClassesPattern: "" + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + excludeAnnotatedProperties: [] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: false MissingWhenCase: active: true + allowElseExpression: true + NullableToStringCall: + active: false RedundantElseInWhen: active: true UnconditionalJumpStatementInLoop: active: false + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: false UnreachableCode: active: true UnsafeCallOnNullableType: active: true UnsafeCast: + active: true + UnusedUnaryOperator: active: false UselessPostfixExpression: active: false @@ -432,30 +589,51 @@ potential-bugs: style: active: true + ClassOrdering: + active: false CollapsibleIfStatements: - active: true + active: false DataClassContainsFunctions: - active: true + active: false conversionFunctionPrefix: 'to' DataClassShouldBeImmutable: active: false + DestructuringDeclarationWithTooManyEntries: + active: false + maxDestructuringEntries: 3 EqualsNullCall: active: true EqualsOnSignatureLine: active: false + ExplicitCollectionElementAccessMethod: + active: false ExplicitItLambdaParameter: active: false ExpressionBodySyntax: - active: true + active: false includeLineWrapping: false ForbiddenComment: active: true - values: 'TODO:,FIXME:,STOPSHIP:' - allowedPatterns: "" + values: + - 'FIXME:' + - 'STOPSHIP:' + - 'TODO:' + allowedPatterns: '' ForbiddenImport: active: false - imports: '' - forbiddenPatterns: "" + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: false + methods: + - 'kotlin.io.print' + - 'kotlin.io.println' + ForbiddenPublicDataClass: + active: true + excludes: ['**'] + ignorePackages: + - '*.internal' + - '*.internal.*' ForbiddenVoid: active: false ignoreOverridden: false @@ -463,30 +641,44 @@ style: FunctionOnlyReturningConstant: active: true ignoreOverridableFunction: true - excludedFunctions: 'describeContents' - excludeAnnotatedFunction: "dagger.Provides" + ignoreActualFunction: true + excludedFunctions: '' + excludeAnnotatedFunction: + - 'dagger.Provides' LibraryCodeMustSpecifyReturnType: active: true + excludes: ['**'] + LibraryEntitiesShouldNotBePublic: + active: true + excludes: ['**'] LoopWithTooManyJumpStatements: active: true maxJumpCount: 1 MagicNumber: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - ignoreNumbers: '-1,0,1,2' + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreNumbers: + - '-1' + - '0' + - '1' + - '2' ignoreHashCodeFunction: true ignorePropertyDeclaration: false + ignoreLocalVariableDeclaration: false ignoreConstantDeclaration: true ignoreCompanionObjectPropertyDeclaration: true ignoreAnnotation: false ignoreNamedArgument: true ignoreEnums: false ignoreRanges: false + ignoreExtensionFunctions: true MandatoryBracesIfStatements: - active: true + active: false + MandatoryBracesLoops: + active: false MaxLineLength: active: true - maxLineLength: 160 #default: 120 + maxLineLength: 120 excludePackageStatements: true excludeImportStatements: true excludeCommentStatements: false @@ -494,12 +686,16 @@ style: active: true ModifierOrder: active: true - NestedClassesVisibility: + MultilineLambdaItParameter: active: false + NestedClassesVisibility: + active: true NewLineAtEndOfFile: active: true NoTabs: active: false + ObjectLiteralToLambda: + active: false OptionalAbstractKeyword: active: true OptionalUnit: @@ -507,29 +703,32 @@ style: OptionalWhenBraces: active: false PreferToOverPairSyntax: - active: true + active: false ProtectedMemberInFinalClass: active: true RedundantExplicitType: - active: true + active: false + RedundantHigherOrderMapUsage: + active: false RedundantVisibilityModifierRule: active: false ReturnCount: active: true - max: 5 - excludedFunctions: "equals" + max: 2 + excludedFunctions: 'equals' excludeLabeled: false excludeReturnFromLambda: true excludeGuardClauses: false SafeCast: active: true SerialVersionUIDInSerializableClass: - active: false + active: true SpacingBetweenPackageAndImports: active: false ThrowsCount: active: true - max: 10 + max: 2 + excludeGuardClauses: false TrailingWhitespace: active: false UnderscoresInNumericLiterals: @@ -537,43 +736,62 @@ style: acceptableDecimalLength: 5 UnnecessaryAbstractClass: active: true - excludeAnnotatedClasses: "dagger.Module" + excludeAnnotatedClasses: + - 'dagger.Module' + UnnecessaryAnnotationUseSiteTarget: + active: false UnnecessaryApply: active: true + UnnecessaryFilter: + active: false UnnecessaryInheritance: active: true UnnecessaryLet: - active: true + active: false UnnecessaryParentheses: - active: true + active: false UntilInsteadOfRangeTo: active: false UnusedImports: - active: true + active: false UnusedPrivateClass: active: true UnusedPrivateMember: active: true - allowedNames: "(_|ignored|expected|serialVersionUID)" + allowedNames: '(_|ignored|expected|serialVersionUID)' UseArrayLiteralsInAnnotations: active: false + UseCheckNotNull: + active: false UseCheckOrError: active: false UseDataClass: active: false - excludeAnnotatedClasses: "" + excludeAnnotatedClasses: [] allowVars: false + UseEmptyCounterpart: + active: false + UseIfEmptyOrIfBlank: + active: false UseIfInsteadOfWhen: active: false + UseIsNullOrEmpty: + active: false + UseOrEmpty: + active: false UseRequire: active: false + UseRequireNotNull: + active: false UselessCallOnNotNull: active: true UtilityClassWithPublicConstructor: active: true VarCouldBeVal: - active: false + active: true WildcardImport: active: true - excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" - excludeImports: 'java.util.*,kotlinx.android.synthetic.*' \ No newline at end of file + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**'] + excludeImports: + - 'java.util.*' + - 'kotlinx.android.synthetic.*' diff --git a/gradle.properties b/gradle.properties index f9842bc..349b5c9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,6 +17,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true -android.jetifier.blacklist=bcprov-jdk15on +android.jetifier.ignorelist=bcprov-jdk15on # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6975649..4ee106d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip diff --git a/gradlescripts/detekt.config.gradle b/gradlescripts/detekt.config.gradle new file mode 100644 index 0000000..3e62590 --- /dev/null +++ b/gradlescripts/detekt.config.gradle @@ -0,0 +1,24 @@ + +detekt { + toolVersion = "$detekt_version" + + source = files( + "$projectDir/app/src/main/java", + "$projectDir/core/src/main/java", + "$projectDir/mockserver/src/main/java", + "$projectDir/model/src/main/java", + "$projectDir/network/src/main/java" + ) + config = files("$projectDir/detekt/detekt.yml") + baseline = file("$projectDir/detekt/baseline.xml") + reports { + txt { + enabled = true + destination = file("build/reports/detekt.txt") + } + html { + enabled = true + destination = file("build/reports/detekt.html") + } + } +} \ No newline at end of file diff --git a/gradlescripts/ktlint.gradle b/gradlescripts/ktlint.gradle new file mode 100644 index 0000000..f61347d --- /dev/null +++ b/gradlescripts/ktlint.gradle @@ -0,0 +1,3 @@ +subprojects { + apply plugin: "org.jlleitschuh.gradle.ktlint" +} \ No newline at end of file diff --git a/gradlescripts/lint.gradle b/gradlescripts/lint.gradle new file mode 100644 index 0000000..d906414 --- /dev/null +++ b/gradlescripts/lint.gradle @@ -0,0 +1,34 @@ +subprojects { module -> + plugins.withType(JavaPlugin).whenPluginAdded { + configure(module) { + apply plugin: "com.android.lint" + lintOptions { + warningsAsErrors true + abortOnError true + textReport true + ignore 'Overdraw' + textOutput "stdout" + } + } + } + + plugins.withId("com.android.application") { + module.android.lintOptions { + warningsAsErrors true + abortOnError true + textReport true + ignore 'Overdraw' + textOutput "stdout" + } + } + + plugins.withId("com.android.library") { + module.android.lintOptions { + warningsAsErrors true + abortOnError true + textReport true + ignore 'Overdraw' + textOutput "stdout" + } + } +} \ No newline at end of file diff --git a/gradlescripts/testoptions.gradle b/gradlescripts/testoptions.gradle new file mode 100644 index 0000000..c7b068b --- /dev/null +++ b/gradlescripts/testoptions.gradle @@ -0,0 +1,44 @@ +subprojects { module -> + plugins.withType(JavaPlugin).whenPluginAdded { + module.test { + useJUnitPlatform() + testLogging { + events 'started', 'passed', 'skipped', 'failed' + exceptionFormat "full" + showStandardStreams true + } + } + } + + plugins.withId("com.android.application") { + module.android.testOptions.unitTests.all { + useJUnitPlatform() + testLogging { + events 'started', 'passed', 'skipped', 'failed' + exceptionFormat "full" + showStandardStreams true + } + } + module.android.testOptions { + unitTests { + includeAndroidResources = true + } + } + } + + plugins.withId("com.android.library") { + module.android.testOptions.unitTests.all { + useJUnitPlatform() + testLogging { + events 'started', 'passed', 'skipped', 'failed' + exceptionFormat "full" + showStandardStreams true + } + } + module.android.testOptions { + unitTests { + includeAndroidResources = true + } + } + } +} \ No newline at end of file diff --git a/model/build.gradle b/model/build.gradle index e30cb2f..a1a673c 100644 --- a/model/build.gradle +++ b/model/build.gradle @@ -6,10 +6,4 @@ plugins { java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 -} - -compileKotlin { - kotlinOptions { - freeCompilerArgs += "-Xinline-classes" - } -} +} \ No newline at end of file diff --git a/network/build.gradle b/network/build.gradle index d49020e..fc17ae7 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -9,15 +9,6 @@ java { targetCompatibility = JavaVersion.VERSION_1_8 } -test { - useJUnitPlatform() - testLogging { - events 'started', 'passed', 'skipped', 'failed' - exceptionFormat "full" - showStandardStreams true - } -} - dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" diff --git a/network/src/test/java/org/fnives/test/showcase/network/auth/CodeKataLoginRemoteSourceTest.kt b/network/src/test/java/org/fnives/test/showcase/network/auth/CodeKataLoginRemoteSourceTest.kt index 1baa1d1..843e61b 100644 --- a/network/src/test/java/org/fnives/test/showcase/network/auth/CodeKataLoginRemoteSourceTest.kt +++ b/network/src/test/java/org/fnives/test/showcase/network/auth/CodeKataLoginRemoteSourceTest.kt @@ -1,24 +1,11 @@ package org.fnives.test.showcase.network.auth import kotlinx.coroutines.runBlocking -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import org.fnives.test.showcase.model.auth.LoginCredentials -import org.fnives.test.showcase.model.network.BaseUrl -import org.fnives.test.showcase.model.session.Session -import org.fnives.test.showcase.network.auth.model.LoginStatusResponses -import org.fnives.test.showcase.network.di.createNetworkModules -import org.fnives.test.showcase.network.session.NetworkSessionLocalStorage -import org.fnives.test.showcase.network.shared.exceptions.NetworkException -import org.fnives.test.showcase.network.shared.exceptions.ParsingException -import org.junit.jupiter.api.* -import org.koin.core.context.startKoin -import org.koin.core.context.stopKoin -import org.koin.test.KoinTest -import org.koin.test.inject -import org.mockito.kotlin.mock -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test import java.io.BufferedReader import java.io.InputStreamReader @@ -27,54 +14,46 @@ class CodeKataLoginRemoteSourceTest { @BeforeEach fun setUp() { - } @AfterEach fun tearDown() { - } @DisplayName("GIVEN successful response WHEN request is fired THEN login status success is returned") @Test fun successResponseIsParsedProperly() = runBlocking { - } @DisplayName("GIVEN successful response WHEN request is fired THEN the request is setup properly") @Test fun requestProperlySetup() = runBlocking { - } @DisplayName("GIVEN bad request response WHEN request is fired THEN login status invalid credentials is returned") @Test fun badRequestMeansInvalidCredentials() = runBlocking { - } @DisplayName("GIVEN_internal_error_response_WHEN_request_is_fired_THEN_network_exception_is_thrown") @Test fun genericErrorMeansNetworkError() { - } @DisplayName("GIVEN invalid json response WHEN request is fired THEN network exception is thrown") @Test fun invalidJsonMeansParsingException() { - } @DisplayName("GIVEN malformed json response WHEN request is fired THEN network exception is thrown") @Test fun malformedJsonMeansParsingException() { - } companion object { internal fun Any.readResourceFile(filePath: String): String = try { BufferedReader(InputStreamReader(this.javaClass.classLoader.getResourceAsStream(filePath)!!)) - .readLines().joinToString("\n") + .readLines().joinToString("\n") } catch (nullPointerException: NullPointerException) { throw IllegalArgumentException("$filePath file not found!", nullPointerException) } @@ -87,6 +66,5 @@ class CodeKataLoginRemoteSourceTest { } while (true) } } - } -} \ No newline at end of file +} diff --git a/network/src/test/java/org/fnives/test/showcase/network/auth/LoginRemoteSourceTest.kt b/network/src/test/java/org/fnives/test/showcase/network/auth/LoginRemoteSourceTest.kt index 0258bf0..cd586bd 100644 --- a/network/src/test/java/org/fnives/test/showcase/network/auth/LoginRemoteSourceTest.kt +++ b/network/src/test/java/org/fnives/test/showcase/network/auth/LoginRemoteSourceTest.kt @@ -12,7 +12,11 @@ import org.fnives.test.showcase.network.session.NetworkSessionLocalStorage import org.fnives.test.showcase.network.shared.MockServerScenarioSetupExtensions import org.fnives.test.showcase.network.shared.exceptions.NetworkException import org.fnives.test.showcase.network.shared.exceptions.ParsingException -import org.junit.jupiter.api.* +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -26,6 +30,7 @@ import org.skyscreamer.jsonassert.JSONCompareMode class LoginRemoteSourceTest : KoinTest { private val sut by inject() + @RegisterExtension @JvmField val mockServerScenarioSetupExtensions = MockServerScenarioSetupExtensions() @@ -76,7 +81,11 @@ class LoginRemoteSourceTest : KoinTest { Assertions.assertEquals(null, request.getHeader("Authorization")) Assertions.assertEquals("/login", request.path) val loginRequest = createExpectedLoginRequestJson("a", "b") - JSONAssert.assertEquals(loginRequest, request.body.readUtf8(), JSONCompareMode.NON_EXTENSIBLE) + JSONAssert.assertEquals( + loginRequest, + request.body.readUtf8(), + JSONCompareMode.NON_EXTENSIBLE + ) } @DisplayName("GIVEN bad request response WHEN request is fired THEN login status invalid credentials is returned") diff --git a/network/src/test/java/org/fnives/test/showcase/network/content/CodeKataSessionExpirationTest.kt b/network/src/test/java/org/fnives/test/showcase/network/content/CodeKataSessionExpirationTest.kt index a977bf4..eb2f310 100644 --- a/network/src/test/java/org/fnives/test/showcase/network/content/CodeKataSessionExpirationTest.kt +++ b/network/src/test/java/org/fnives/test/showcase/network/content/CodeKataSessionExpirationTest.kt @@ -1,21 +1,21 @@ package org.fnives.test.showcase.network.content import kotlinx.coroutines.runBlocking -import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import org.fnives.test.showcase.model.network.BaseUrl -import org.fnives.test.showcase.model.session.Session -import org.fnives.test.showcase.network.auth.CodeKataLoginRemoteSourceTest.Companion.readResourceFile import org.fnives.test.showcase.network.di.createNetworkModules import org.fnives.test.showcase.network.session.NetworkSessionExpirationListener import org.fnives.test.showcase.network.session.NetworkSessionLocalStorage -import org.fnives.test.showcase.network.shared.exceptions.NetworkException -import org.junit.jupiter.api.* +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test import org.koin.core.context.startKoin import org.koin.core.context.stopKoin import org.koin.test.KoinTest import org.koin.test.inject -import org.mockito.kotlin.* +import org.mockito.kotlin.mock @Disabled("CodeKata") class CodeKataSessionExpirationTest : KoinTest { @@ -33,12 +33,12 @@ class CodeKataSessionExpirationTest : KoinTest { mockNetworkSessionExpirationListener = mock() startKoin { modules( - createNetworkModules( - baseUrl = BaseUrl(mockWebServer.url("mockserver/").toString()), - enableLogging = true, - networkSessionExpirationListenerProvider = { mockNetworkSessionExpirationListener }, - networkSessionLocalStorageProvider = { mockNetworkSessionLocalStorage } - ).toList() + createNetworkModules( + baseUrl = BaseUrl(mockWebServer.url("mockserver/").toString()), + enableLogging = true, + networkSessionExpirationListenerProvider = { mockNetworkSessionExpirationListener }, + networkSessionLocalStorageProvider = { mockNetworkSessionLocalStorage } + ).toList() ) } } @@ -57,6 +57,5 @@ class CodeKataSessionExpirationTest : KoinTest { @DisplayName("GIVEN 401 THEN failing refresh WHEN content requested THE error is returned and callback is Called") @Test fun failingRefreshResultsInSessionExpiration() = runBlocking { - } -} \ No newline at end of file +} diff --git a/network/src/test/java/org/fnives/test/showcase/network/content/SessionExpirationTest.kt b/network/src/test/java/org/fnives/test/showcase/network/content/SessionExpirationTest.kt index e63924d..0f80d3e 100644 --- a/network/src/test/java/org/fnives/test/showcase/network/content/SessionExpirationTest.kt +++ b/network/src/test/java/org/fnives/test/showcase/network/content/SessionExpirationTest.kt @@ -11,7 +11,11 @@ import org.fnives.test.showcase.network.session.NetworkSessionExpirationListener import org.fnives.test.showcase.network.session.NetworkSessionLocalStorage import org.fnives.test.showcase.network.shared.MockServerScenarioSetupExtensions import org.fnives.test.showcase.network.shared.exceptions.NetworkException -import org.junit.jupiter.api.* +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -64,35 +68,39 @@ class SessionExpirationTest : KoinTest { @DisplayName("GIVEN_401_THEN_refresh_token_ok_response_WHEN_content_requested_THE_tokens_are_refreshed_and_request_retried_with_new_tokens") @Test fun successRefreshResultsInRequestRetry() = runBlocking { - var sessionToReturnByMock: Session? = ContentData.loginSuccessResponse - mockServerScenarioSetup.setScenario( - ContentScenario.Unauthorized(false) - .then(ContentScenario.Success(true)), - false - ) - mockServerScenarioSetup.setScenario(RefreshTokenScenario.Success, false) - whenever(mockNetworkSessionLocalStorage.session).doAnswer { sessionToReturnByMock } - doAnswer { sessionToReturnByMock = it.arguments[0] as Session? } - .whenever(mockNetworkSessionLocalStorage).session = anyOrNull() + var sessionToReturnByMock: Session? = ContentData.loginSuccessResponse + mockServerScenarioSetup.setScenario( + ContentScenario.Unauthorized(false) + .then(ContentScenario.Success(true)), + false + ) + mockServerScenarioSetup.setScenario(RefreshTokenScenario.Success, false) + whenever(mockNetworkSessionLocalStorage.session).doAnswer { sessionToReturnByMock } + doAnswer { sessionToReturnByMock = it.arguments[0] as Session? } + .whenever(mockNetworkSessionLocalStorage).session = anyOrNull() - sut.get() + sut.get() - mockServerScenarioSetup.takeRequest() - val refreshRequest = mockServerScenarioSetup.takeRequest() - val retryAfterTokenRefreshRequest = mockServerScenarioSetup.takeRequest() + mockServerScenarioSetup.takeRequest() + val refreshRequest = mockServerScenarioSetup.takeRequest() + val retryAfterTokenRefreshRequest = mockServerScenarioSetup.takeRequest() - Assertions.assertEquals("PUT", refreshRequest.method) - Assertions.assertEquals("/login/${ContentData.loginSuccessResponse.refreshToken}", refreshRequest.path) - Assertions.assertEquals(null, refreshRequest.getHeader("Authorization")) - Assertions.assertEquals("Android", refreshRequest.getHeader("Platform")) - Assertions.assertEquals("", refreshRequest.body.readUtf8()) - Assertions.assertEquals( - ContentData.refreshSuccessResponse.accessToken, - retryAfterTokenRefreshRequest.getHeader("Authorization") - ) - verify(mockNetworkSessionLocalStorage, times(1)).session = ContentData.refreshSuccessResponse - verifyZeroInteractions(mockNetworkSessionExpirationListener) - } + Assertions.assertEquals("PUT", refreshRequest.method) + Assertions.assertEquals( + "/login/${ContentData.loginSuccessResponse.refreshToken}", + refreshRequest.path + ) + Assertions.assertEquals(null, refreshRequest.getHeader("Authorization")) + Assertions.assertEquals("Android", refreshRequest.getHeader("Platform")) + Assertions.assertEquals("", refreshRequest.body.readUtf8()) + Assertions.assertEquals( + ContentData.refreshSuccessResponse.accessToken, + retryAfterTokenRefreshRequest.getHeader("Authorization") + ) + verify(mockNetworkSessionLocalStorage, times(1)).session = + ContentData.refreshSuccessResponse + verifyZeroInteractions(mockNetworkSessionExpirationListener) + } @DisplayName("GIVEN 401 THEN failing refresh WHEN content requested THE error is returned and callback is Called") @Test