diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a3b8bc6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +root = true + +# See detektConfig.yml, values should be the same + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 120 + +ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^ \ No newline at end of file diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index cd91ace..a6c8d57 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -1,23 +1,27 @@ name: Android CI on: - push: - branches: [ master ] - pull_request: - branches: [ master ] + push: + branches: [ master ] + pull_request: + branches: [ master ] jobs: - build: + build: - runs-on: ubuntu-latest + runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Run tests - run: ./gradlew test - - name: Build a library with Gradle - run: ./gradlew assembleRelease + steps: + - uses: actions/checkout@v3 + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: '17' + cache: 'gradle' + - name: Detekt + run: make detekt + - name: Run tests + run: ./gradlew test + - name: Build a library with Gradle + run: ./gradlew assembleRelease diff --git a/.gitignore b/.gitignore index 03001d6..2cc838f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ /.idea .DS_Store /build +/build-logic/build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..deb3464 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +path := ./ + +detekt: + $(path)gradlew detektAll diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 0000000..bc0172f --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + `kotlin-dsl` +} diff --git a/build-logic/checks/.gitignore b/build-logic/checks/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/build-logic/checks/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/build-logic/checks/build.gradle.kts b/build-logic/checks/build.gradle.kts new file mode 100644 index 0000000..26fe76a --- /dev/null +++ b/build-logic/checks/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + `kotlin-dsl` +} + +// group = "com.avito.android.buildlogic" + +dependencies { + implementation(projects.gradleExt) + implementation(libs.detektGradle) + // workaround for https://github.com/gradle/gradle/issues/15383 + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) +} diff --git a/build-logic/checks/src/main/kotlin/convention.detekt.gradle.kts b/build-logic/checks/src/main/kotlin/convention.detekt.gradle.kts new file mode 100644 index 0000000..1588991 --- /dev/null +++ b/build-logic/checks/src/main/kotlin/convention.detekt.gradle.kts @@ -0,0 +1,40 @@ +import io.gitlab.arturbosch.detekt.Detekt +import ru.beryukhov.android.withVersionCatalog + +plugins { + /** + * https://docs.gradle.org/current/userguide/base_plugin.html + * base plugin added to add wiring on check->build tasks + */ + base + id("io.gitlab.arturbosch.detekt") +} + +// workaround for https://github.com/gradle/gradle/issues/15383 +project.withVersionCatalog { libs -> + dependencies { + detektPlugins(libs.detektFormatting) + } +} + +val detektAll = tasks.register("detektAll") { + description = "Runs over whole code base without the starting overhead for each module." + parallel = true + setSource(files(projectDir)) + + config.setFrom(files(project.rootDir.resolve("detektConfig.yml"))) + buildUponDefaultConfig = false + + include("**/*.kt") + include("**/*.kts") + exclude("**/resources/**") + exclude("**/build/**") + reports { + xml.required.set(false) + html.required.set(false) + } +} + +tasks.named("check").configure { + dependsOn(detektAll) +} diff --git a/build-logic/gradle-ext/.gitignore b/build-logic/gradle-ext/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/build-logic/gradle-ext/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/build-logic/gradle-ext/build.gradle.kts b/build-logic/gradle-ext/build.gradle.kts new file mode 100644 index 0000000..8fc3714 --- /dev/null +++ b/build-logic/gradle-ext/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + `kotlin-dsl` +} + +// group = "com.avito.android.buildlogic" + +dependencies { + // workaround for https://github.com/gradle/gradle/issues/15383 + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) +} diff --git a/build-logic/gradle-ext/src/main/kotlin/ru/beryukhov/android/VersionCatalog.kt b/build-logic/gradle-ext/src/main/kotlin/ru/beryukhov/android/VersionCatalog.kt new file mode 100644 index 0000000..b16a94e --- /dev/null +++ b/build-logic/gradle-ext/src/main/kotlin/ru/beryukhov/android/VersionCatalog.kt @@ -0,0 +1,16 @@ +package ru.beryukhov.android + +import org.gradle.accessors.dm.LibrariesForLibs +import org.gradle.api.Project +import org.gradle.kotlin.dsl.the + +/** + * workaround to make version catalog accessible in convention plugins + * https://github.com/gradle/gradle/issues/15383 + */ +fun Project.withVersionCatalog(block: (libs: LibrariesForLibs) -> Unit) { + if (project.name != "gradle-kotlin-dsl-accessors") { + val libs = the() + block.invoke(libs) + } +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..38765d9 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,24 @@ +rootProject.name = "build-logic" + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } + + repositories { + google() + mavenCentral() + } +} + +include("checks") +/** + * renamed from 'gradle' to prevent IDE resolution conflict: + * usages of "typesafe project accessors", e.g. `projects.gradle.someProject` was red in IDE + * build was fine however + */ +include("gradle-ext") diff --git a/build.gradle.kts b/build.gradle.kts index ffb0891..ffd0a2f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,29 +1,24 @@ -buildscript { - val kotlin_version by extra("1.5.20") - val coroutines_version by extra("1.5.1") - val robolectric_version by extra("4.6.1") +plugins { + id("convention.detekt") + // id("io.github.gradle-nexus.publish-plugin") version "1.1.0" +} +buildscript { repositories { google() mavenCentral() + maven("https://plugins.gradle.org/m2/") } dependencies { - classpath("com.android.tools.build:gradle:7.0.1") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") + classpath(libs.androidGradle) + classpath(libs.kotlinGradle) + classpath(libs.bintrayGradle) } } - allprojects { repositories { google() mavenCentral() + maven("https://dl.bintray.com/andreyberyukhov/FlowReactiveNetwork") } } - -plugins { - id("io.github.gradle-nexus.publish-plugin") version "1.1.0" -} - -task("clean") { - delete(rootProject.buildDir) -} \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index cff9fbd..e242d33 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -4,4 +4,4 @@ plugins { repositories { google() mavenCentral() -} \ No newline at end of file +} diff --git a/detektConfig.yml b/detektConfig.yml new file mode 100644 index 0000000..52ce421 --- /dev/null +++ b/detektConfig.yml @@ -0,0 +1,748 @@ +build: + maxIssues: 0 + excludeCorrectable: false + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: true + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ClassCountProcessor' + # - 'PackageCountProcessor' + # - 'KtFileCountProcessor' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + # - 'FindingsReport' + - 'FileBasedFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + +comments: + active: true + # https://detekt.github.io/detekt/comments.html#absentorwrongfilelicense + # we don't use licenses per file, only root one + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + # https://detekt.github.io/detekt/comments.html#commentoverprivatefunction + CommentOverPrivateFunction: + active: false + # https://detekt.github.io/detekt/comments.html#commentoverprivateproperty + CommentOverPrivateProperty: + active: false + # https://detekt.github.io/detekt/comments.html#endofsentenceformat + EndOfSentenceFormat: + active: false + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + # https://detekt.github.io/detekt/comments.html#undocumentedpublicclass + UndocumentedPublicClass: + active: false + searchInNestedClass: false + searchInInnerClass: false + searchInInnerObject: false + searchInInnerInterface: false + # https://detekt.github.io/detekt/comments.html#undocumentedpublicfunction + UndocumentedPublicFunction: + active: false + # https://detekt.github.io/detekt/comments.html#undocumentedpublicproperty + UndocumentedPublicProperty: + active: false + +complexity: + active: false + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: false + threshold: 10 + includeStaticDeclarations: false + includePrivateDeclarations: false + CyclomaticComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: false + ignoreSimpleWhenEntries: false + ignoreNestingFunctions: false + nestingFunctions: [ run, let, apply, with, also, use, forEach, isNotNull, ifNull ] + LabeledExpression: + active: false + ignoredLabels: [ ] + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: false + ignoreDataClasses: true + ignoreAnnotated: [ ] + MethodOverloading: + active: false + threshold: 6 + NamedArguments: + active: false + threshold: 3 + NestedBlockDepth: + active: true + threshold: 4 + ReplaceSafeCallChainWithRun: + active: false + StringLiteralDuplication: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + excludes: [ '**/test/**', '**/androidTest/**' ] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: false + ignorePrivate: false + ignoreOverridden: false + +coroutines: + active: true + GlobalCoroutineUsage: + active: false + InjectDispatcher: + active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunSwallowedCancellation: + active: false + SuspendFunWithCoroutineScopeReceiver: + active: false + SuspendFunWithFlowReturnType: + active: true + +empty-blocks: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptycatchblock + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + # https://detekt.github.io/detekt/empty-blocks.html#emptyclassblock + EmptyClassBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptydefaultconstructor + EmptyDefaultConstructor: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptydowhileblock + EmptyDoWhileBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptyelseblock + EmptyElseBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptyfinallyblock + EmptyFinallyBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptyforblock + EmptyForBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptyfunctionblock + # todo enable, 26 errors + EmptyFunctionBlock: + active: false + ignoreOverridden: false + # https://detekt.github.io/detekt/empty-blocks.html#emptyifblock + EmptyIfBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptyinitblock + EmptyInitBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptyktfile + EmptyKtFile: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptysecondaryconstructor + EmptySecondaryConstructor: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptytryblock + EmptyTryBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptywhenblock + EmptyWhenBlock: + active: true + # https://detekt.github.io/detekt/empty-blocks.html#emptywhileblock + EmptyWhileBlock: + active: true + +exceptions: + active: false + ExceptionRaisedInUnexpectedLocation: + active: false + methodNames: [ toString, hashCode, equals, finalize ] + InstanceOfCheckForException: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + NotImplementedDeclaration: + active: false + PrintStackTrace: + active: false + RethrowCaughtException: + active: false + ReturnFromFinally: + active: false + ignoreLabeled: false + SwallowedException: + active: false + ignoredExceptionTypes: + - InterruptedException + - NumberFormatException + - ParseException + - MalformedURLException + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: false + ThrowingExceptionInMain: + active: false + ThrowingExceptionsWithoutMessageOrCause: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + exceptions: + - IllegalArgumentException + - IllegalStateException + - IOException + ThrowingNewInstanceOfSameException: + active: false + TooGenericExceptionCaught: + active: true + excludes: [ '**/test/**', '**/androidTest/**' ] + exceptionNames: + - ArrayIndexOutOfBoundsException + - Error + - Exception + - IllegalMonitorStateException + - NullPointerException + - IndexOutOfBoundsException + - RuntimeException + - Throwable + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - Error + - Exception + - Throwable + - RuntimeException + +formatting: + active: true + android: true + autoCorrect: false + + # todo rise an issue: false positive on kotlin @file annotations + AnnotationOnSeparateLine: + active: false + AnnotationSpacing: + active: true + # todo fix and enable + ArgumentListWrapping: + active: false + # questionable rule; && and || goes to the end of line, instead of beginning a new line as we do right now + ChainWrapping: + active: false + CommentSpacing: + active: true + # duplicate of naming:EnumNaming + EnumEntryNameCase: + active: false + # todo what is it? + Filename: + active: false + # DUPLICATE of style:NewLineAtEndOfFile + FinalNewline: + active: false + insertFinalNewLine: false + ImportOrdering: + active: true + layout: '*,java.**,javax.**,kotlin.**,^' + # blocked by bugs: https://github.com/pinterest/ktlint/issues?q=is%3Aissue+is%3Aopen+Indentation + Indentation: + active: false + indentSize: 4 + # DUPLICATE of style:MaxLineLength + MaximumLineLength: + active: false + maxLineLength: 120 + # https://ktlint.github.io/#rule-modifier-order + ModifierOrdering: + active: true + MultiLineIfElse: + active: true + NoBlankLineBeforeRbrace: + active: true + # https://ktlint.github.io/#rule-blank + NoConsecutiveBlankLines: + active: true + # https://ktlint.github.io/#rule-empty-class-body + NoEmptyClassBody: + active: true + # questionable rule, it is good idea to have some visual space after function declaration + NoEmptyFirstLineInMethodBlock: + active: false + NoLineBreakAfterElse: + active: true + NoLineBreakBeforeAssignment: + active: true + NoMultipleSpaces: + active: true + # https://ktlint.github.io/#rule-semi + NoSemicolons: + active: true + # https://ktlint.github.io/#rule-trailing-whitespaces + NoTrailingSpaces: + active: true + NoUnitReturn: + active: true + + # DUPLICATE of style UnusedImports + NoUnusedImports: + active: false + # DUPLICATE of style WildcardImports + NoWildcardImports: + active: false + + # DUPLICATE of naming:PackageNaming rule + PackageName: + active: false + ParameterListWrapping: + active: true + + # https://ktlint.github.io/#rule-spacing + SpacingAroundColon: + active: true + SpacingAroundComma: + active: true + SpacingAroundCurly: + active: true + SpacingAroundDot: + active: true + SpacingAroundDoubleColon: + active: true + SpacingAroundKeyword: + active: true + SpacingAroundOperators: + active: true + SpacingAroundParens: + active: true + SpacingAroundRangeOperator: + active: true + # https://detekt.github.io/detekt/formatting.html#spacingbetweendeclarationswithannotations + SpacingBetweenDeclarationsWithAnnotations: + active: false + # https://detekt.github.io/detekt/formatting.html#spacingbetweendeclarationswithcomments + SpacingBetweenDeclarationsWithComments: + active: true + # https://ktlint.github.io/#rule-string-template + StringTemplate: + active: true + +naming: + active: true + # https://detekt.github.io/detekt/naming.html#classnaming + ClassNaming: + active: true + classPattern: '[A-Z][a-zA-Z0-9]*' + # https://detekt.github.io/detekt/naming.html#constructorparameternaming + ConstructorParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + EnumNaming: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + forbiddenName: [ ] + FunctionMaxLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + maximumFunctionNameLength: 30 + # blocked by `Is` functions + FunctionMinLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + minimumFunctionNameLength: 3 + # blocked by `Is` functions + FunctionNaming: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' + excludeClassPattern: '$^' + ignoreAnnotated: [ 'Composable' ] + FunctionParameterNaming: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + # TODO: enable + InvalidPackageDeclaration: + active: false + rootPackage: '' + # https://detekt.github.io/detekt/naming.html#matchingdeclarationname + MatchingDeclarationName: + active: true + mustBeFirst: true + # https://detekt.github.io/detekt/naming.html#membernameequalsclassname + MemberNameEqualsClassName: + active: false + ignoreOverridden: true + NonBooleanPropertyPrefixedWithIs: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + ObjectPropertyNaming: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + 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/**' ] + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + 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/**' ] + maximumVariableNameLength: 64 + VariableMinLength: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + minimumVariableNameLength: 1 + # https://detekt.github.io/detekt/naming.html#variablenaming + VariableNaming: + active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + +performance: + active: false + ArrayPrimitive: + active: true + ForEachOnRange: + active: true + excludes: [ '**/test/**', '**/androidTest/**' ] + SpreadOperator: + active: true + excludes: [ '**/test/**', '**/androidTest/**' ] + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: false + Deprecation: + active: false + EqualsAlwaysReturnsTrueOrFalse: + active: false + EqualsWithHashCodeExist: + active: false + ExplicitGarbageCollectionCall: + active: false + HasPlatformType: + active: false + IgnoredReturnValue: + active: false + restrictToConfig: true + returnValueAnnotations: [ '*.CheckReturnValue', '*.CheckResult' ] + ImplicitDefaultLocale: + active: false + ImplicitUnitReturnType: + active: false + allowExplicitReturnType: true + InvalidRange: + active: false + IteratorHasNextCallsNextMethod: + active: false + IteratorNotThrowingNoSuchElementException: + active: false + LateinitUsage: + active: false + excludes: [ '**/test/**', '**/androidTest/**' ] + ignoreAnnotated: [ ] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: false + NullableToStringCall: + active: false + UnconditionalJumpStatementInLoop: + active: false + UnnecessaryNotNullOperator: + active: false + UnnecessarySafeCall: + active: false + UnreachableCode: + active: false + UnsafeCallOnNullableType: + active: false + UnsafeCast: + active: false + UselessPostfixExpression: + active: false + WrongEqualsTypeParameter: + active: false + +style: + active: true + BracesOnIfStatements: + active: true + singleLine: 'never' + multiLine: 'always' + # https://detekt.github.io/detekt/style.html#classordering + ClassOrdering: + active: true + # https://detekt.github.io/detekt/style.html#collapsibleifstatements + # questionable rule, no need for now + CollapsibleIfStatements: + active: false + # https://detekt.github.io/detekt/style.html#dataclasscontainsfunctions + # probably a good idea, but seems too strict + DataClassContainsFunctions: + active: false + conversionFunctionPrefix: [ 'to' ] + # https://detekt.github.io/detekt/style.html#dataclassshouldbeimmutable + # todo probably a good idea to enable it + DataClassShouldBeImmutable: + active: false + # https://detekt.github.io/detekt/style.html#equalsnullcall + EqualsNullCall: + active: true + # https://detekt.github.io/detekt/style.html#equalsonsignatureline + EqualsOnSignatureLine: + active: true + # https://detekt.github.io/detekt/style.html#explicitcollectionelementaccessmethod + ExplicitCollectionElementAccessMethod: + active: true + # https://detekt.github.io/detekt/style.html#explicititlambdaparameter + ExplicitItLambdaParameter: + active: true + # https://detekt.github.io/detekt/style.html#expressionbodysyntax + # sometimes it's harder to read + ExpressionBodySyntax: + active: false + includeLineWrapping: true + # https://detekt.github.io/detekt/style.html#forbiddencomment + ForbiddenComment: + active: true + comments: [ 'STOPSHIP' ] + allowedPatterns: '' + # https://detekt.github.io/detekt/style.html#forbiddenimport + # todo maybe use it to ban junit 4 in test code + ForbiddenImport: + active: true + imports: [ ] + forbiddenPatterns: 'gradle.kotlin.dsl.accessors.*' + # https://detekt.github.io/detekt/style.html#forbiddenmethodcall + # needs type resolution config https://github.com/detekt/detekt/issues/2259 + ForbiddenMethodCall: + active: false + methods: [ 'kotlin.io.println', 'kotlin.io.print' ] + # https://detekt.github.io/detekt/style.html#forbiddenvoid + # needs type resolution config https://github.com/detekt/detekt/issues/2259 + ForbiddenVoid: + active: false + ignoreOverridden: false + ignoreUsageInGenerics: false + # https://detekt.github.io/detekt/style.html#functiononlyreturningconstant + FunctionOnlyReturningConstant: + active: false + ignoreOverridableFunction: true + excludedFunctions: [ 'describeContents' ] + ignoreAnnotated: [ 'dagger.Provides' ] + # https://detekt.github.io/detekt/style.html#loopwithtoomanyjumpstatements + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + # https://detekt.github.io/detekt/style.html#magicnumber + MagicNumber: + active: false + excludes: [ '**/build.gradle.kts', '**/test/**', '**/androidTest/**' ] + ignoreNumbers: [ '-1', '0', '1', '2' ] + ignoreHashCodeFunction: true + ignorePropertyDeclaration: true + ignoreLocalVariableDeclaration: true + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: true + ignoreNamedArgument: true + ignoreEnums: true + ignoreRanges: false + # https://detekt.github.io/detekt/style.html#mandatorybracesloops + MandatoryBracesLoops: + active: true + # https://detekt.github.io/detekt/style.html#maxlinelength + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: true + # https://detekt.github.io/detekt/style.html#maybeconst + MayBeConst: + active: true + # https://detekt.github.io/detekt/style.html#modifierorder + ModifierOrder: + active: true + # https://detekt.github.io/detekt/style.html#nestedclassesvisibility + NestedClassesVisibility: + active: true + # https://detekt.github.io/detekt/style.html#newlineatendoffile + NewLineAtEndOfFile: + active: true + # https://detekt.github.io/detekt/style.html#notabs + NoTabs: + active: true + # https://detekt.github.io/detekt/style.html#optionalabstractkeyword + OptionalAbstractKeyword: + active: true + # https://detekt.github.io/detekt/style.html#optionalunit + OptionalUnit: + active: false + BracesOnWhenStatements: + active: true + # https://detekt.github.io/detekt/style.html#prefertooverpairsyntax + PreferToOverPairSyntax: + active: true + # https://detekt.github.io/detekt/style.html#protectedmemberinfinalclass + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: false + RedundantHigherOrderMapUsage: + active: false + # https://detekt.github.io/detekt/style.html#redundantvisibilitymodifierrule + # todo don't know about kotlin strict mode + # fix in 1.15 https://github.com/detekt/detekt/issues/3125 only works per module, not in our detektAll task + # because of how strict api detection works + RedundantVisibilityModifierRule: + active: false + # https://detekt.github.io/detekt/style.html#returncount + # todo enable (11 errors) + ReturnCount: + active: false + max: 2 + excludedFunctions: [ 'equals' ] + excludeLabeled: false + excludeReturnFromLambda: true + excludeGuardClauses: false + # https://detekt.github.io/detekt/style.html#safecast + SafeCast: + active: false + SerialVersionUIDInSerializableClass: + active: false + SpacingBetweenPackageAndImports: + active: false + ThrowsCount: + active: false + max: 2 + TrailingWhitespace: + active: false + UnderscoresInNumericLiterals: + active: false + acceptableLength: 5 + UnnecessaryAbstractClass: + active: false + ignoreAnnotated: [ 'dagger.Module' ] + UnnecessaryAnnotationUseSiteTarget: + active: false + UnnecessaryApply: + active: false + UnnecessaryInheritance: + active: false + UnnecessaryLet: + active: false + # https://detekt.github.io/detekt/style.html#unnecessaryparentheses + UnnecessaryParentheses: + active: true + UntilInsteadOfRangeTo: + active: false + # https://detekt.github.io/detekt/style.html#unusedimports + UnusedImports: + active: true + # https://detekt.github.io/detekt/style.html#unusedprivateclass + UnusedPrivateClass: + active: true + # https://detekt.github.io/detekt/style.html#unusedprivatemember + UnusedPrivateMember: + active: true + allowedNames: '(_|ignored|expected|serialVersionUID)' + # https://detekt.github.io/detekt/style.html#usearrayliteralsinannotations + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: false + UseCheckOrError: + active: false + UseDataClass: + active: false + ignoreAnnotated: [ ] + allowVars: false + UseEmptyCounterpart: + active: false + UseIfEmptyOrIfBlank: + active: false + UseIfInsteadOfWhen: + active: false + UseRequire: + active: false + UseRequireNotNull: + active: false + UselessCallOnNotNull: + active: false + UtilityClassWithPublicConstructor: + active: false + # https://detekt.github.io/detekt/style.html#varcouldbeval + VarCouldBeVal: + active: true + # https://detekt.github.io/detekt/style.html#wildcardimport + WildcardImport: + active: true + excludes: [ ] + excludeImports: [ ] diff --git a/gradle.properties b/gradle.properties index 5c41815..e335d2e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,24 +1,7 @@ -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app"s APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -XX:+UseParallelGC android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true -# Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official +org.gradle.configuration-cache=true bintrayuser=replaceme bintraykey=replaceme \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..7b00b2c --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,40 @@ +[versions] +publishedLibVersion = "1.0.2" + +targetSdk = "33" +compileSdk = "34" +minSdk = "21" + +kotlin = "1.9.23" # https://kotlinlang.org/docs/releases.html#release-details +agp = "8.2.2" # https://developer.android.com/studio/releases/gradle-plugin +bintray = "1.8.5" + +coroutines = "1.8.0" # https://github.com/Kotlin/kotlinx.coroutines + +detekt = "1.23.6" # https://github.com/detekt/detekt + +[libraries] +kotlinGradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +androidGradle = { module = "com.android.tools.build:gradle", version.ref = "agp" } +bintrayGradle = { module = "com.jfrog.bintray.gradle:gradle-bintray-plugin", version.ref = "bintray" } +detektGradle = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } + +detektFormatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } +coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } +coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } + +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } + +coreKtx = "androidx.core:core-ktx:1.12.0" +material = "com.google.android.material:material:1.11.0" + +truth = "com.google.truth:truth:1.0.1" +robolectric = "org.robolectric:robolectric:4.12" +mockk = "io.mockk:mockk:1.13.10" # https://github.com/mockk/mockk +turbine = "app.cash.turbine:turbine:1.1.0" + +androidx-annotation = "androidx.annotation:annotation:1.7.1" # https://mvnrepository.com/artifact/androidx.annotation/annotation +androidx-test="androidx.test:core:1.5.0" + +flowreactivenetwork = { module = "ru.beryukhov:flowreactivenetwork", version.ref = "publishedLibVersion" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 82015a7..559efb4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon May 11 17:59:52 MSK 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip diff --git a/reactiveNetwork/build.gradle.kts b/reactiveNetwork/build.gradle.kts index 0727a96..a962cd0 100644 --- a/reactiveNetwork/build.gradle.kts +++ b/reactiveNetwork/build.gradle.kts @@ -1,69 +1,55 @@ plugins { id("com.android.library") kotlin("android") + `maven-publish` } android { - compileSdk = 29 - //testOptions { unitTests { includeAndroidResources = true } } + namespace = "ru.beryukhov.reactivenetwork" + + compileSdk = libs.versions.compileSdk.get().toInt() + testOptions.unitTests.isIncludeAndroidResources = true defaultConfig { - minSdk = 14 + minSdk = libs.versions.minSdk.get().toInt() } compileOptions { - targetCompatibility = JavaVersion.VERSION_1_8 - sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" } buildTypes { - getByName("release") { + release { isMinifyEnabled = false - proguardFile(getDefaultProguardFile("proguard-android.txt")) - proguardFile("proguard-rules.pro") + proguardFiles( + getDefaultProguardFile("proguard-android.txt"), + "proguard-rules.pro" + ) } } - sourceSets["main"].java { - srcDir("src/main/kotlin") - } - - sourceSets["test"].java { - srcDir("src/test/kotlin") - } - kotlin { explicitApi() } +} - kotlinOptions { - freeCompilerArgs = freeCompilerArgs + "-Xopt-in=kotlin.RequiresOptIn" - } - - dependencies { - val kotlin_version = rootProject.extra["kotlin_version"] - val coroutines_version = rootProject.extra["coroutines_version"] - val robolectric_version = rootProject.extra["robolectric_version"] - - implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version") - - implementation("androidx.annotation:annotation:1.2.0") - - - testImplementation ("org.jetbrains.kotlin:kotlin-test-common:$kotlin_version") - testImplementation ("org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlin_version") +dependencies { - testImplementation ("com.google.truth:truth:1.0.1") - testImplementation ("org.robolectric:robolectric:$robolectric_version") - testImplementation ("io.mockk:mockk:1.12.0") + implementation(libs.coroutines.core) + implementation(libs.coroutines.android) - testImplementation ("org.jetbrains.kotlin:kotlin-test:$kotlin_version") - testImplementation ("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version") - testImplementation ("androidx.test:core:1.4.0") - } + implementation(libs.androidx.annotation) + testImplementation(libs.kotlin.test) + testImplementation(libs.coroutines.test) + testImplementation(libs.truth) + testImplementation(libs.robolectric) + testImplementation(libs.mockk) + testImplementation(libs.turbine) + testImplementation(libs.androidx.test) } -apply {from("${rootProject.projectDir}/scripts/publish-root.gradle")} -apply {from("${rootProject.projectDir}/scripts/publish-module.gradle")} +// apply {from("${rootProject.projectDir}/scripts/publish-root.gradle")} +// apply {from("${rootProject.projectDir}/scripts/publish-module.gradle")} diff --git a/reactiveNetwork/src/main/AndroidManifest.xml b/reactiveNetwork/src/main/AndroidManifest.xml index de77853..bd478b8 100644 --- a/reactiveNetwork/src/main/AndroidManifest.xml +++ b/reactiveNetwork/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Connectivity.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Connectivity.kt index 36a7f50..e3f48d4 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Connectivity.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Connectivity.kt @@ -8,24 +8,24 @@ import android.net.NetworkInfo.DetailedState /** * Connectivity class represents current connectivity status. It wraps NetworkInfo object. */ -data class Connectivity( - val state : NetworkInfo.State = NetworkInfo.State.DISCONNECTED, - val detailedState : DetailedState? = DetailedState.IDLE, - val type : Int = UNKNOWN_TYPE, - val subType : Int = UNKNOWN_SUB_TYPE, - val available : Boolean = false, - val failover : Boolean = false, - val roaming : Boolean = false, - val typeName : String? = "NONE", - val subTypeName : String? = "NONE", - val reason : String? = "", - val extraInfo : String? = "" -){ - companion object { - const val UNKNOWN_TYPE = -1 - const val UNKNOWN_SUB_TYPE = -1 +public data class Connectivity( + val state: NetworkInfo.State = NetworkInfo.State.DISCONNECTED, + val detailedState: DetailedState? = DetailedState.IDLE, + val type: Int = UNKNOWN_TYPE, + val subType: Int = UNKNOWN_SUB_TYPE, + val available: Boolean = false, + val failover: Boolean = false, + val roaming: Boolean = false, + val typeName: String? = "NONE", + val subTypeName: String? = "NONE", + val reason: String? = "", + val extraInfo: String? = "" +) { + public companion object { + public const val UNKNOWN_TYPE: Int = -1 + public const val UNKNOWN_SUB_TYPE: Int = -1 - fun create(context: Context): Connectivity { + public fun create(context: Context): Connectivity { Preconditions.checkNotNull(context, "context == null") return create( context, @@ -52,18 +52,18 @@ data class Connectivity( private fun create(networkInfo: NetworkInfo): Connectivity { return Connectivity( - state=networkInfo.state, - detailedState=networkInfo.detailedState, - type=networkInfo.type, - subType=networkInfo.subtype, - available=networkInfo.isAvailable, - failover=networkInfo.isFailover, - roaming=networkInfo.isRoaming, - typeName=networkInfo.typeName, - subTypeName=networkInfo.subtypeName, - reason=networkInfo.reason, - extraInfo=networkInfo.extraInfo + state = networkInfo.state, + detailedState = networkInfo.detailedState, + type = networkInfo.type, + subType = networkInfo.subtype, + available = networkInfo.isAvailable, + failover = networkInfo.isFailover, + roaming = networkInfo.isRoaming, + typeName = networkInfo.typeName, + subTypeName = networkInfo.subtypeName, + reason = networkInfo.reason, + extraInfo = networkInfo.extraInfo ) } } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/ConnectivityPredicate.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/ConnectivityPredicate.kt index 84dfa09..d28f8bf 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/ConnectivityPredicate.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/ConnectivityPredicate.kt @@ -6,7 +6,7 @@ import android.net.NetworkInfo * ConnectivityPredicate is a class containing predefined methods, which can be used for filtering * reactive streams of network connectivity */ -object ConnectivityPredicate { +public object ConnectivityPredicate { /** * Filter, which returns true if at least one given state occurred * @@ -14,7 +14,7 @@ object ConnectivityPredicate { * @return true if at least one given state occurred */ @JvmStatic - fun hasState(vararg states: NetworkInfo.State): Predicate { + public fun hasState(vararg states: NetworkInfo.State): Predicate { return object : Predicate { @Throws(Exception::class) override fun test(connectivity: Connectivity): Boolean { @@ -35,7 +35,7 @@ object ConnectivityPredicate { * @return true if at least one given type occurred */ @JvmStatic - fun hasType(vararg types: Int): Predicate { + public fun hasType(vararg types: Int): Predicate { val extendedTypes = appendUnknownNetworkTypeToTypes(types) return object : Predicate { @@ -60,7 +60,7 @@ object ConnectivityPredicate { * @return types of the network with unknown type as an array of ints */ @JvmStatic - fun appendUnknownNetworkTypeToTypes(types: IntArray): IntArray { + public fun appendUnknownNetworkTypeToTypes(types: IntArray): IntArray { var i = 0 val extendedTypes = IntArray(types.size + 1) for (type in types) { @@ -70,4 +70,4 @@ object ConnectivityPredicate { extendedTypes[i] = Connectivity.UNKNOWN_TYPE return extendedTypes } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Preconditions.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Preconditions.kt index f1ffe4a..86a544d 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Preconditions.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Preconditions.kt @@ -1,15 +1,16 @@ package ru.beryukhov.reactivenetwork import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast -object Preconditions { +public object Preconditions { /** * Validation method, which checks if an object is null * * @param o object to verify * @param message to be thrown in exception */ - fun checkNotNull(o: Any?, message: String) { + public fun checkNotNull(o: Any?, message: String) { if (o == null) { throw IllegalArgumentException(message) } @@ -21,8 +22,8 @@ object Preconditions { * @param string to verify * @param message to be thrown in exception */ - fun checkNotNullOrEmpty(string: String?, message:String) { - if (string == null || string.isEmpty()) { + public fun checkNotNullOrEmpty(string: String?, message: String) { + if (string.isNullOrEmpty()) { throw IllegalArgumentException(message) } } @@ -33,7 +34,7 @@ object Preconditions { * @param number integer to verify * @param message to be thrown in exception */ - fun checkGreaterOrEqualToZero(number: Int, message: String) { + public fun checkGreaterOrEqualToZero(number: Int, message: String) { if (number < 0) { throw IllegalArgumentException(message) } @@ -45,7 +46,7 @@ object Preconditions { * @param number integer to verify * @param message to be thrown in exception */ - fun checkGreaterThanZero(number: Int, message: String) { + public fun checkGreaterThanZero(number: Int, message: String) { if (number <= 0) { throw IllegalArgumentException(message) } @@ -57,8 +58,8 @@ object Preconditions { * * @return boolean true if current Android version is Lollipop or higher */ - @androidx.annotation.ChecksSdkIntAtLeast(api = Build.VERSION_CODES.LOLLIPOP) - fun isAtLeastAndroidLollipop(): Boolean { + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.LOLLIPOP) + public fun isAtLeastAndroidLollipop(): Boolean { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP } @@ -68,8 +69,8 @@ object Preconditions { * * @return boolean true if current Android version is Marshmallow or higher */ - @androidx.annotation.ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M) - fun isAtLeastAndroidMarshmallow():Boolean { + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M) + public fun isAtLeastAndroidMarshmallow(): Boolean { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Predicate.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Predicate.kt index 603c743..38a66f0 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Predicate.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/Predicate.kt @@ -4,7 +4,7 @@ package ru.beryukhov.reactivenetwork * A functional interface (callback) that returns true or false for the given input value. * @param the first value */ -interface Predicate { +public interface Predicate { /** * Test the given input value and return a boolean. * @param t the value @@ -12,5 +12,5 @@ interface Predicate { * @throws Exception on error */ @Throws(Exception::class) - fun test(t: T): Boolean -} \ No newline at end of file + public fun test(t: T): Boolean +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/ReactiveNetwork.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/ReactiveNetwork.kt index 99bbbda..2728571 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/ReactiveNetwork.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/ReactiveNetwork.kt @@ -17,7 +17,7 @@ import ru.beryukhov.reactivenetwork.network.observing.strategy.PreLollipopNetwor * listening network connection state and change of the WiFi signal strength * with Coroutines Flow. It was backported from ReactiveNetwork with Java and RxJava inside. */ -class ReactiveNetwork { +public class ReactiveNetwork { /** * Observes network connectivity. Information about network state, type and typeName are contained * in @@ -28,14 +28,16 @@ class ReactiveNetwork { * type and typeName */ @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) - fun observeNetworkConnectivity(context: Context): Flow { + public fun observeNetworkConnectivity(context: Context): Flow { val strategy: NetworkObservingStrategy = when { Preconditions.isAtLeastAndroidMarshmallow() -> { MarshmallowNetworkObservingStrategy() } + Preconditions.isAtLeastAndroidLollipop() -> { LollipopNetworkObservingStrategy() } + else -> { PreLollipopNetworkObservingStrategy() } @@ -55,7 +57,7 @@ class ReactiveNetwork { * type and typeName */ @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) - fun observeNetworkConnectivity( + public fun observeNetworkConnectivity( context: Context, strategy: NetworkObservingStrategy ): Flow { @@ -75,7 +77,7 @@ class ReactiveNetwork { * and false if not */ @RequiresPermission(Manifest.permission.INTERNET) - fun observeInternetConnectivity(): Flow { + public fun observeInternetConnectivity(): Flow { val settings = InternetObservingSettings.create() return observeInternetConnectivity( settings.strategy(), settings.initialInterval(), @@ -92,7 +94,7 @@ class ReactiveNetwork { * not */ @RequiresPermission(Manifest.permission.INTERNET) - fun observeInternetConnectivity( + public fun observeInternetConnectivity( settings: InternetObservingSettings ): Flow { return observeInternetConnectivity( @@ -142,7 +144,7 @@ class ReactiveNetwork { * and false if not */ @RequiresPermission(Manifest.permission.INTERNET) - suspend fun checkInternetConnectivity(): Boolean { + public suspend fun checkInternetConnectivity(): Boolean { val settings = InternetObservingSettings.create() return checkInternetConnectivity( settings.strategy(), settings.host(), settings.port(), @@ -158,7 +160,7 @@ class ReactiveNetwork { * not */ @RequiresPermission(Manifest.permission.INTERNET) - suspend fun checkInternetConnectivity(settings: InternetObservingSettings): Boolean { + public suspend fun checkInternetConnectivity(settings: InternetObservingSettings): Boolean { return checkInternetConnectivity( settings.strategy(), settings.host(), settings.port(), settings.timeout(), settings.httpResponse(), settings.errorHandler() @@ -179,9 +181,11 @@ class ReactiveNetwork { */ @RequiresPermission(Manifest.permission.INTERNET) internal suspend fun checkInternetConnectivity( - strategy: InternetObservingStrategy, - host: String, port: Int, timeoutInMs: Int, httpResponse: Int, + host: String, + port: Int, + timeoutInMs: Int, + httpResponse: Int, errorHandler: ErrorHandler ): Boolean { checkStrategyIsNotNull(strategy) @@ -198,7 +202,7 @@ class ReactiveNetwork { Preconditions.checkNotNull(strategy, "strategy == null") } - companion object { - const val LOG_TAG = "ReactiveNetwork" + public companion object { + public const val LOG_TAG: String = "ReactiveNetwork" } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/TickerFlow.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/TickerFlow.kt index dd4980e..1ac64e5 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/TickerFlow.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/TickerFlow.kt @@ -1,12 +1,11 @@ package ru.beryukhov.reactivenetwork -import java.util.Timer -import kotlin.concurrent.schedule -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import java.util.Timer +import kotlin.concurrent.schedule /** * Creates a flow that produces the first item after the given initial delay and subsequent items with the @@ -19,7 +18,6 @@ import kotlinx.coroutines.flow.callbackFlow * @param period period between each element in milliseconds. * @param initialDelay delay after which the first element will be produced (it is equal to [period] by default) in milliseconds. */ -@OptIn(ExperimentalCoroutinesApi::class) internal fun tickerFlow( period: Long, initialDelay: Long = period diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingSettings.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingSettings.kt index fecdf88..4666f23 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingSettings.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingSettings.kt @@ -10,7 +10,7 @@ import java.net.HttpURLConnection * We should use its Builder for creating new settings */ // I want to have the same method names as variable names on purpose -class InternetObservingSettings private constructor( +public class InternetObservingSettings private constructor( private val initialInterval: Int, private val interval: Int, private val host: String, @@ -29,60 +29,60 @@ class InternetObservingSettings private constructor( /** * @return initial ping interval in milliseconds */ - fun initialInterval(): Int { + public fun initialInterval(): Int { return initialInterval } /** * @return ping interval in milliseconds */ - fun interval(): Int { + public fun interval(): Int { return interval } /** * @return ping host */ - fun host(): String { + public fun host(): String { return host } /** * @return ping port */ - fun port(): Int { + public fun port(): Int { return port } /** * @return ping timeout in milliseconds */ - fun timeout(): Int { + public fun timeout(): Int { return timeout } - fun httpResponse(): Int { + public fun httpResponse(): Int { return httpResponse } /** * @return error handler for pings and connections */ - fun errorHandler(): ErrorHandler { + public fun errorHandler(): ErrorHandler { return errorHandler } /** * @return internet observing strategy */ - fun strategy(): InternetObservingStrategy { + public fun strategy(): InternetObservingStrategy { return strategy } /** * Settings builder, which contains default parameters */ - class Builder internal constructor() { + public class Builder internal constructor() { internal var initialInterval = 0 internal var interval = 2000 internal var host = "http://clients3.google.com/generate_204" @@ -92,13 +92,14 @@ class InternetObservingSettings private constructor( internal var errorHandler: ErrorHandler = DefaultErrorHandler() internal var strategy: InternetObservingStrategy = WalledGardenInternetObservingStrategy() + /** * sets initial ping interval in milliseconds * * @param initialInterval in milliseconds * @return Builder */ - fun initialInterval(initialInterval: Int): Builder { + public fun initialInterval(initialInterval: Int): Builder { this.initialInterval = initialInterval return this } @@ -109,7 +110,7 @@ class InternetObservingSettings private constructor( * @param interval in milliseconds * @return Builder */ - fun interval(interval: Int): Builder { + public fun interval(interval: Int): Builder { this.interval = interval return this } @@ -119,7 +120,7 @@ class InternetObservingSettings private constructor( * * @return Builder */ - fun host(host: String): Builder { + public fun host(host: String): Builder { this.host = host return this } @@ -129,7 +130,7 @@ class InternetObservingSettings private constructor( * * @return Builder */ - fun port(port: Int): Builder { + public fun port(port: Int): Builder { this.port = port return this } @@ -140,7 +141,7 @@ class InternetObservingSettings private constructor( * @param timeout in milliseconds * @return Builder */ - fun timeout(timeout: Int): Builder { + public fun timeout(timeout: Int): Builder { this.timeout = timeout return this } @@ -151,7 +152,7 @@ class InternetObservingSettings private constructor( * @param httpResponse as integer * @return Builder */ - fun httpResponse(httpResponse: Int): Builder { + public fun httpResponse(httpResponse: Int): Builder { this.httpResponse = httpResponse return this } @@ -161,7 +162,7 @@ class InternetObservingSettings private constructor( * * @return Builder */ - fun errorHandler(errorHandler: ErrorHandler): Builder { + public fun errorHandler(errorHandler: ErrorHandler): Builder { this.errorHandler = errorHandler return this } @@ -172,21 +173,21 @@ class InternetObservingSettings private constructor( * @param strategy for observing and internet connection * @return Builder */ - fun strategy(strategy: InternetObservingStrategy): Builder { + public fun strategy(strategy: InternetObservingStrategy): Builder { this.strategy = strategy return this } - fun build(): InternetObservingSettings { + public fun build(): InternetObservingSettings { return InternetObservingSettings(this) } } - companion object { + public companion object { /** * @return settings with default parameters */ - fun create(): InternetObservingSettings { + public fun create(): InternetObservingSettings { return Builder() .build() } @@ -195,9 +196,8 @@ class InternetObservingSettings private constructor( * Creates builder object * @return Builder */ - fun builder(): Builder { + public fun builder(): Builder { return Builder() } } - -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingStrategy.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingStrategy.kt index 20a72ab..f0efc3d 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingStrategy.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingStrategy.kt @@ -7,7 +7,7 @@ import ru.beryukhov.reactivenetwork.internet.observing.error.ErrorHandler * Internet observing strategy allows to implement different strategies for monitoring connectivity * with the Internet. */ -interface InternetObservingStrategy { +public interface InternetObservingStrategy { /** * Observes connectivity with the Internet by opening socket connection with remote host in a * given interval infinitely @@ -22,7 +22,7 @@ interface InternetObservingStrategy { * @return Flow with Boolean - true, when we have connection with host and false if * not */ - fun observeInternetConnectivity( + public fun observeInternetConnectivity( initialIntervalInMs: Int, intervalInMs: Int, host: String, @@ -39,12 +39,15 @@ interface InternetObservingStrategy { * @param port for checking Internet connectivity * @param timeoutInMs for pinging remote host in milliseconds * @param errorHandler for handling errors while checking connectivity - * @return Boolean - true, when we have connection with host and false if + * @return Boolean - true, when we have connection with host and false if * not */ - suspend fun checkInternetConnectivity( - host: String, port: Int, - timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler + public suspend fun checkInternetConnectivity( + host: String, + port: Int, + timeoutInMs: Int, + httpResponse: Int, + errorHandler: ErrorHandler ): Boolean /** @@ -52,5 +55,5 @@ interface InternetObservingStrategy { * * @return String with a ping host used in the current strategy */ - fun getDefaultPingHost(): String -} \ No newline at end of file + public fun getDefaultPingHost(): String +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/DefaultErrorHandler.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/DefaultErrorHandler.kt index 1cdf16e..0482096 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/DefaultErrorHandler.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/DefaultErrorHandler.kt @@ -3,7 +3,7 @@ package ru.beryukhov.reactivenetwork.internet.observing.error import android.util.Log import ru.beryukhov.reactivenetwork.ReactiveNetwork -class DefaultErrorHandler : +public class DefaultErrorHandler : ErrorHandler { override fun handleError( exception: Exception?, @@ -11,4 +11,4 @@ class DefaultErrorHandler : ) { Log.e(ReactiveNetwork.LOG_TAG, message, exception) } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/ErrorHandler.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/ErrorHandler.kt index a2f7a26..be327f1 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/ErrorHandler.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/ErrorHandler.kt @@ -1,5 +1,5 @@ package ru.beryukhov.reactivenetwork.internet.observing.error -interface ErrorHandler { - fun handleError(exception: Exception?, message: String?) -} \ No newline at end of file +public interface ErrorHandler { + public fun handleError(exception: Exception?, message: String?) +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/SocketInternetObservingStrategy.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/SocketInternetObservingStrategy.kt index 1c98539..0726fc2 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/SocketInternetObservingStrategy.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/SocketInternetObservingStrategy.kt @@ -1,8 +1,5 @@ package ru.beryukhov.reactivenetwork.internet.observing.strategy -import java.io.IOException -import java.net.InetSocketAddress -import java.net.Socket import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -10,12 +7,15 @@ import ru.beryukhov.reactivenetwork.Preconditions import ru.beryukhov.reactivenetwork.internet.observing.InternetObservingStrategy import ru.beryukhov.reactivenetwork.internet.observing.error.ErrorHandler import ru.beryukhov.reactivenetwork.tickerFlow +import java.io.IOException +import java.net.InetSocketAddress +import java.net.Socket /** * Socket strategy for monitoring connectivity with the Internet. * It monitors Internet connectivity via opening socket connection with the remote host. */ -class SocketInternetObservingStrategy : InternetObservingStrategy { +public class SocketInternetObservingStrategy : InternetObservingStrategy { override fun getDefaultPingHost(): String { return DEFAULT_HOST } @@ -43,7 +43,6 @@ class SocketInternetObservingStrategy : InternetObservingStrategy { period = intervalInMs.toLong(), initialDelay = initialIntervalInMs.toLong() ).map { isConnected(adjustedHost, port, timeoutInMs, errorHandler) }.distinctUntilChanged() - } override suspend fun checkInternetConnectivity( @@ -77,10 +76,7 @@ class SocketInternetObservingStrategy : InternetObservingStrategy { return host } - private fun checkGeneralPreconditions( - host: String, port: Int, timeoutInMs: Int, - errorHandler: ErrorHandler - ) { + private fun checkGeneralPreconditions(host: String, port: Int, timeoutInMs: Int, errorHandler: ErrorHandler) { Preconditions.checkNotNullOrEmpty( host, "host is null or empty" @@ -108,10 +104,7 @@ class SocketInternetObservingStrategy : InternetObservingStrategy { * @param errorHandler error handler for socket connection * @return boolean true if connected and false if not */ - internal fun isConnected( - host: String?, port: Int, timeoutInMs: Int, - errorHandler: ErrorHandler - ): Boolean { + internal fun isConnected(host: String?, port: Int, timeoutInMs: Int, errorHandler: ErrorHandler): Boolean { val socket = Socket() return isConnected(socket, host, port, timeoutInMs, errorHandler) } @@ -147,10 +140,10 @@ class SocketInternetObservingStrategy : InternetObservingStrategy { } } - companion object { + private companion object { private const val EMPTY_STRING = "" private const val DEFAULT_HOST = "www.google.com" private const val HTTP_PROTOCOL = "http://" private const val HTTPS_PROTOCOL = "https://" } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/WalledGardenInternetObservingStrategy.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/WalledGardenInternetObservingStrategy.kt index 9ba7652..bdb35b2 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/WalledGardenInternetObservingStrategy.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/WalledGardenInternetObservingStrategy.kt @@ -1,9 +1,5 @@ package ru.beryukhov.reactivenetwork.internet.observing.strategy -import java.io.IOException -import java.net.HttpURLConnection -import java.net.URL -import javax.net.ssl.HttpsURLConnection import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -11,6 +7,10 @@ import ru.beryukhov.reactivenetwork.Preconditions import ru.beryukhov.reactivenetwork.internet.observing.InternetObservingStrategy import ru.beryukhov.reactivenetwork.internet.observing.error.ErrorHandler import ru.beryukhov.reactivenetwork.tickerFlow +import java.io.IOException +import java.net.HttpURLConnection +import java.net.URL +import javax.net.ssl.HttpsURLConnection /** * Walled Garden Strategy for monitoring connectivity with the Internet. @@ -19,14 +19,17 @@ import ru.beryukhov.reactivenetwork.tickerFlow * are generated. Instead HTTP 200 (OK), we got HTTP 204 (NO CONTENT), but it still can tell us * if a device is connected to the Internet or not. */ -class WalledGardenInternetObservingStrategy : InternetObservingStrategy { +public class WalledGardenInternetObservingStrategy : InternetObservingStrategy { override fun getDefaultPingHost(): String { return DEFAULT_HOST } override fun observeInternetConnectivity( initialIntervalInMs: Int, - intervalInMs: Int, host: String, port: Int, timeoutInMs: Int, + intervalInMs: Int, + host: String, + port: Int, + timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler ): Flow { @@ -71,7 +74,9 @@ class WalledGardenInternetObservingStrategy : InternetObservingStrategy { ) ) { HTTPS_PROTOCOL + host - } else host + } else { + host + } } private fun checkGeneralPreconditions( @@ -164,9 +169,9 @@ class WalledGardenInternetObservingStrategy : InternetObservingStrategy { return urlConnection } - companion object { + private companion object { private const val DEFAULT_HOST = "http://clients3.google.com/generate_204" private const val HTTP_PROTOCOL = "http://" private const val HTTPS_PROTOCOL = "https://" } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/NetworkObservingStrategy.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/NetworkObservingStrategy.kt index 6b01c11..bfcd958 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/NetworkObservingStrategy.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/NetworkObservingStrategy.kt @@ -8,14 +8,14 @@ import ru.beryukhov.reactivenetwork.Connectivity * Network observing strategy allows to implement different strategies for monitoring network * connectivity change. Network monitoring API may differ depending of specific Android version. */ -interface NetworkObservingStrategy { +public interface NetworkObservingStrategy { /** * Observes network connectivity * * @param context of the Activity or an Application * @return Observable representing stream of the network connectivity */ - fun observeNetworkConnectivity(context: Context): Flow + public fun observeNetworkConnectivity(context: Context): Flow /** * Handles errors, which occurred during observing network connectivity @@ -23,5 +23,5 @@ interface NetworkObservingStrategy { * @param message to be processed * @param exception which was thrown */ - fun onError(message: String, exception: Exception) -} \ No newline at end of file + public fun onError(message: String, exception: Exception) +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/LollipopNetworkObservingStrategy.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/LollipopNetworkObservingStrategy.kt index 26e4af3..41f99fb 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/LollipopNetworkObservingStrategy.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/LollipopNetworkObservingStrategy.kt @@ -7,7 +7,6 @@ import android.net.ConnectivityManager.NetworkCallback import android.net.Network import android.net.NetworkRequest import android.util.Log -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow @@ -22,11 +21,10 @@ import ru.beryukhov.reactivenetwork.network.observing.NetworkObservingStrategy * Uses Network Callback API. */ @TargetApi(21) -class LollipopNetworkObservingStrategy : NetworkObservingStrategy { +public class LollipopNetworkObservingStrategy : NetworkObservingStrategy { // it has to be initialized in the Observable due to Context private lateinit var networkCallback: NetworkCallback - @OptIn(ExperimentalCoroutinesApi::class) override fun observeNetworkConnectivity(context: Context): Flow { val service = Context.CONNECTIVITY_SERVICE val manager = context.getSystemService(service) as ConnectivityManager @@ -62,5 +60,4 @@ class LollipopNetworkObservingStrategy : NetworkObservingStrategy { ) { Log.e(ReactiveNetwork.LOG_TAG, message, exception) } - -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/MarshmallowNetworkObservingStrategy.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/MarshmallowNetworkObservingStrategy.kt index f630271..d0fcdc2 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/MarshmallowNetworkObservingStrategy.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/MarshmallowNetworkObservingStrategy.kt @@ -13,13 +13,13 @@ import android.net.NetworkInfo import android.net.NetworkRequest import android.os.PowerManager import android.util.Log -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.onCompletion +import kotlinx.coroutines.flow.onStart import ru.beryukhov.reactivenetwork.Connectivity import ru.beryukhov.reactivenetwork.ReactiveNetwork import ru.beryukhov.reactivenetwork.network.observing.NetworkObservingStrategy @@ -28,15 +28,15 @@ import ru.beryukhov.reactivenetwork.network.observing.NetworkObservingStrategy * Network observing strategy for devices with Android Marshmallow (API 23) or higher. * Uses Network Callback API and handles Doze mode. */ + @TargetApi(23) -class MarshmallowNetworkObservingStrategy : NetworkObservingStrategy { +public class MarshmallowNetworkObservingStrategy : NetworkObservingStrategy { // it has to be initialized in the Observable due to Context - private var networkCallback: NetworkCallback? = null - private var connectivitySubject: MutableStateFlow? = null + private lateinit var networkCallback: NetworkCallback + private val connectivitySubject = MutableSharedFlow() private val idleReceiver: BroadcastReceiver = createIdleBroadcastReceiver() private var lastConnectivity = Connectivity() - @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) override fun observeNetworkConnectivity(context: Context): Flow { val service = Context.CONNECTIVITY_SERVICE val manager = context.getSystemService(service) as ConnectivityManager @@ -46,14 +46,18 @@ class MarshmallowNetworkObservingStrategy : NetworkObservingStrategy { NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) .build() - manager.registerNetworkCallback(request, networkCallback!!) - connectivitySubject = MutableStateFlow(Connectivity.create(context)) - return connectivitySubject!!.flatMapConcat { connectivity -> + manager.registerNetworkCallback(request, networkCallback) + return connectivitySubject.flatMapConcat { connectivity -> propagateAnyConnectedState(lastConnectivity, connectivity) - }.onCompletion { + }.onStart { emit(Connectivity.create(context)) } + .onCompletion { + tryToUnregisterCallback(manager) + tryToUnregisterReceiver(context) + } + .onCompletion { tryToUnregisterCallback(manager) tryToUnregisterReceiver(context) - } + }.distinctUntilChanged() } internal fun propagateAnyConnectedState( @@ -126,11 +130,11 @@ class MarshmallowNetworkObservingStrategy : NetworkObservingStrategy { internal fun createNetworkCallback(context: Context): NetworkCallback { return object : NetworkCallback() { - override fun onAvailable(network: Network?) { + override fun onAvailable(network: Network) { onNext(Connectivity.create(context)) } - override fun onLost(network: Network?) { + override fun onLost(network: Network) { onNext(Connectivity.create(context)) } } @@ -140,9 +144,9 @@ class MarshmallowNetworkObservingStrategy : NetworkObservingStrategy { connectivitySubject?.tryEmit(connectivity) } - companion object { + internal companion object { internal const val ERROR_MSG_NETWORK_CALLBACK: String = "could not unregister network callback" internal const val ERROR_MSG_RECEIVER: String = "could not unregister receiver" } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/PreLollipopNetworkObservingStrategy.kt b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/PreLollipopNetworkObservingStrategy.kt index d8feea0..5dfb7c6 100644 --- a/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/PreLollipopNetworkObservingStrategy.kt +++ b/reactiveNetwork/src/main/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/PreLollipopNetworkObservingStrategy.kt @@ -6,14 +6,11 @@ import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager import android.util.Log -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.onStart import ru.beryukhov.reactivenetwork.Connectivity import ru.beryukhov.reactivenetwork.ReactiveNetwork import ru.beryukhov.reactivenetwork.network.observing.NetworkObservingStrategy @@ -22,8 +19,8 @@ import ru.beryukhov.reactivenetwork.network.observing.NetworkObservingStrategy * Network observing strategy for Android devices before Lollipop (API 20 or lower). * Uses Broadcast Receiver. */ -class PreLollipopNetworkObservingStrategy : NetworkObservingStrategy { - @OptIn(ExperimentalCoroutinesApi::class) +public class PreLollipopNetworkObservingStrategy : NetworkObservingStrategy { + override fun observeNetworkConnectivity(context: Context): Flow { val filter = IntentFilter() filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION) @@ -38,13 +35,9 @@ class PreLollipopNetworkObservingStrategy : NetworkObservingStrategy { } context.registerReceiver(receiver, filter) awaitClose { - GlobalScope.launch { - withContext(Dispatchers.Main) { - tryToUnregisterReceiver(context, receiver) - } - } + tryToUnregisterReceiver(context, receiver) } - } + }.onStart { emit(Connectivity.create(context)) }.distinctUntilChanged() } internal fun tryToUnregisterReceiver( @@ -64,4 +57,4 @@ class PreLollipopNetworkObservingStrategy : NetworkObservingStrategy { ) { Log.e(ReactiveNetwork.LOG_TAG, message, exception) } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/ConnectivityTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/ConnectivityTest.kt index f038ee4..b2ae22a 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/ConnectivityTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/ConnectivityTest.kt @@ -5,7 +5,7 @@ import android.net.ConnectivityManager import android.net.NetworkInfo import android.net.NetworkInfo.DetailedState import androidx.test.core.app.ApplicationProvider -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -20,27 +20,28 @@ class ConnectivityTest { fun shouldCreateConnectivity() { // when val connectivity = Connectivity() // then - Truth.assertThat(connectivity).isNotNull() - Truth.assertThat(connectivity.state) + assertThat(connectivity).isNotNull() + assertThat(connectivity.state) .isEqualTo(NetworkInfo.State.DISCONNECTED) - Truth.assertThat(connectivity.detailedState) + assertThat(connectivity.detailedState) .isEqualTo(DetailedState.IDLE) - Truth.assertThat(connectivity.type).isEqualTo(Connectivity.UNKNOWN_TYPE) - Truth.assertThat(connectivity.subType).isEqualTo(Connectivity.UNKNOWN_SUB_TYPE) - Truth.assertThat(connectivity.available).isFalse() - Truth.assertThat(connectivity.failover).isFalse() - Truth.assertThat(connectivity.roaming).isFalse() - Truth.assertThat(connectivity.typeName) + assertThat(connectivity.type).isEqualTo(Connectivity.UNKNOWN_TYPE) + assertThat(connectivity.subType).isEqualTo(Connectivity.UNKNOWN_SUB_TYPE) + assertThat(connectivity.available).isFalse() + assertThat(connectivity.failover).isFalse() + assertThat(connectivity.roaming).isFalse() + assertThat(connectivity.typeName) .isEqualTo(TYPE_NAME_NONE) - Truth.assertThat(connectivity.subTypeName) + assertThat(connectivity.subTypeName) .isEqualTo(TYPE_NAME_NONE) - Truth.assertThat(connectivity.reason).isEmpty() - Truth.assertThat(connectivity.extraInfo).isEmpty() + assertThat(connectivity.reason).isEmpty() + assertThat(connectivity.extraInfo).isEmpty() } @Test @Throws(Exception::class) - fun stateShouldBeEqualToGivenValue() { // given + fun stateShouldBeEqualToGivenValue() { + // given val connectivity = Connectivity( state = NetworkInfo.State.CONNECTED, type = ConnectivityManager.TYPE_WIFI, @@ -52,12 +53,13 @@ class ConnectivityTest { hasState(connectivity.state) val shouldBeEqualToGivenStatus = equalTo.test(connectivity) // then - Truth.assertThat(shouldBeEqualToGivenStatus).isTrue() + assertThat(shouldBeEqualToGivenStatus).isTrue() } @Test @Throws(Exception::class) - fun stateShouldBeEqualToOneOfGivenMultipleValues() { // given + fun stateShouldBeEqualToOneOfGivenMultipleValues() { + // given val connectivity = Connectivity( state = NetworkInfo.State.CONNECTING, type = ConnectivityManager.TYPE_WIFI, @@ -69,44 +71,53 @@ class ConnectivityTest { val equalTo = hasState(*states) val shouldBeEqualToGivenStatus = equalTo.test(connectivity) // then - Truth.assertThat(shouldBeEqualToGivenStatus).isTrue() + assertThat(shouldBeEqualToGivenStatus).isTrue() } @Test @Throws(Exception::class) - fun stateShouldNotBeEqualToGivenValue() { // given - val connectivity = Connectivity(state=NetworkInfo.State.DISCONNECTED, - type=ConnectivityManager.TYPE_WIFI, - typeName=TYPE_NAME_WIFI) + fun stateShouldNotBeEqualToGivenValue() { + // given + val connectivity = Connectivity( + state = NetworkInfo.State.DISCONNECTED, + type = ConnectivityManager.TYPE_WIFI, + typeName = TYPE_NAME_WIFI + ) // when val equalTo = hasState(NetworkInfo.State.CONNECTED) val shouldBeEqualToGivenStatus = equalTo.test(connectivity) // then - Truth.assertThat(shouldBeEqualToGivenStatus).isFalse() + assertThat(shouldBeEqualToGivenStatus).isFalse() } @Test @Throws(Exception::class) - fun typeShouldBeEqualToGivenValue() { // given - val connectivity = Connectivity(state=NetworkInfo.State.CONNECTED, - type=ConnectivityManager.TYPE_WIFI, - typeName=TYPE_NAME_WIFI) + fun typeShouldBeEqualToGivenValue() { + // given + val connectivity = Connectivity( + state = NetworkInfo.State.CONNECTED, + type = ConnectivityManager.TYPE_WIFI, + typeName = TYPE_NAME_WIFI + ) // note that unknown type is added initially by the ConnectivityPredicate#hasType method val givenTypes = intArrayOf(connectivity.type, Connectivity.UNKNOWN_TYPE) // when val equalTo = hasType(*givenTypes) val shouldBeEqualToGivenStatus = equalTo.test(connectivity) // then - Truth.assertThat(shouldBeEqualToGivenStatus).isTrue() + assertThat(shouldBeEqualToGivenStatus).isTrue() } @Test @Throws(Exception::class) - fun typeShouldBeEqualToOneOfGivenMultipleValues() { // given - val connectivity = Connectivity(state=NetworkInfo.State.CONNECTING, - type=ConnectivityManager.TYPE_MOBILE, - typeName=TYPE_NAME_MOBILE) + fun typeShouldBeEqualToOneOfGivenMultipleValues() { + // given + val connectivity = Connectivity( + state = NetworkInfo.State.CONNECTING, + type = ConnectivityManager.TYPE_MOBILE, + typeName = TYPE_NAME_MOBILE + ) // note that unknown type is added initially by the ConnectivityPredicate#hasType method val givenTypes = intArrayOf( @@ -118,15 +129,18 @@ class ConnectivityTest { val equalTo = hasType(*givenTypes) val shouldBeEqualToGivenStatus = equalTo.test(connectivity) // then - Truth.assertThat(shouldBeEqualToGivenStatus).isTrue() + assertThat(shouldBeEqualToGivenStatus).isTrue() } @Test @Throws(Exception::class) - fun typeShouldNotBeEqualToGivenValue() { // given - val connectivity = Connectivity(state=NetworkInfo.State.CONNECTED, - type=ConnectivityManager.TYPE_WIFI, - typeName=TYPE_NAME_WIFI) + fun typeShouldNotBeEqualToGivenValue() { + // given + val connectivity = Connectivity( + state = NetworkInfo.State.CONNECTED, + type = ConnectivityManager.TYPE_WIFI, + typeName = TYPE_NAME_WIFI + ) // note that unknown type is added initially by the ConnectivityPredicate#hasType method val givenTypes = intArrayOf(ConnectivityManager.TYPE_MOBILE, Connectivity.UNKNOWN_TYPE) @@ -134,31 +148,34 @@ class ConnectivityTest { val equalTo = hasType(*givenTypes) val shouldBeEqualToGivenStatus = equalTo.test(connectivity) // then - Truth.assertThat(shouldBeEqualToGivenStatus).isFalse() + assertThat(shouldBeEqualToGivenStatus).isFalse() } @Test - fun theSameConnectivityObjectsShouldBeEqual() { // given + fun theSameConnectivityObjectsShouldBeEqual() { + // given val connectivityOne = Connectivity() val connectivityTwo = Connectivity() // when val objectsAreEqual = connectivityOne == connectivityTwo // then - Truth.assertThat(objectsAreEqual).isTrue() + assertThat(objectsAreEqual).isTrue() } @Test - fun twoDefaultObjectsShouldBeInTheSameBucket() { // given + fun twoDefaultObjectsShouldBeInTheSameBucket() { + // given val connectivityOne = Connectivity() val connectivityTwo = Connectivity() // when val hashCodesAreEqual = connectivityOne.hashCode() == connectivityTwo.hashCode() // then - Truth.assertThat(hashCodesAreEqual).isTrue() + assertThat(hashCodesAreEqual).isTrue() } @Test - fun shouldAppendUnknownTypeWhileFilteringNetworkTypesInsidePredicate() { // given + fun shouldAppendUnknownTypeWhileFilteringNetworkTypesInsidePredicate() { + // given val types = intArrayOf(ConnectivityManager.TYPE_MOBILE, ConnectivityManager.TYPE_WIFI) val expectedOutputTypes = intArrayOf( @@ -170,21 +187,23 @@ class ConnectivityTest { val outputTypes = appendUnknownNetworkTypeToTypes(types) // then - Truth.assertThat(outputTypes).isEqualTo(expectedOutputTypes) + assertThat(outputTypes).isEqualTo(expectedOutputTypes) } @Test - fun shouldAppendUnknownTypeWhileFilteringNetworkTypesInsidePredicateForEmptyArray() { // given + fun shouldAppendUnknownTypeWhileFilteringNetworkTypesInsidePredicateForEmptyArray() { + // given val types = intArrayOf() val expectedOutputTypes = intArrayOf(Connectivity.UNKNOWN_TYPE) // when val outputTypes = appendUnknownNetworkTypeToTypes(types) // then - Truth.assertThat(outputTypes).isEqualTo(expectedOutputTypes) + assertThat(outputTypes).isEqualTo(expectedOutputTypes) } @Test - fun shouldCreateConnectivityWithBuilder() { // given + fun shouldCreateConnectivityWithBuilder() { + // given val state = NetworkInfo.State.CONNECTED val detailedState = DetailedState.CONNECTED val type = ConnectivityManager.TYPE_WIFI @@ -194,85 +213,93 @@ class ConnectivityTest { val reason = "no reason" val extraInfo = "extra info" // when - val connectivity = Connectivity(state=state, - detailedState=detailedState, - type=type, - subType=subType, - available=true, - failover=false, - roaming=true, - typeName=typeName, - subTypeName=subTypeName, - reason=reason, - extraInfo=extraInfo) + val connectivity = Connectivity( + state = state, + detailedState = detailedState, + type = type, + subType = subType, + available = true, + failover = false, + roaming = true, + typeName = typeName, + subTypeName = subTypeName, + reason = reason, + extraInfo = extraInfo + ) // then - Truth.assertThat(connectivity.state).isEqualTo(state) - Truth.assertThat(connectivity.detailedState).isEqualTo(detailedState) - Truth.assertThat(connectivity.type).isEqualTo(type) - Truth.assertThat(connectivity.subType).isEqualTo(subType) - Truth.assertThat(connectivity.available).isTrue() - Truth.assertThat(connectivity.failover).isFalse() - Truth.assertThat(connectivity.roaming).isTrue() - Truth.assertThat(connectivity.typeName).isEqualTo(typeName) - Truth.assertThat(connectivity.subTypeName).isEqualTo(subTypeName) - Truth.assertThat(connectivity.reason).isEqualTo(reason) - Truth.assertThat(connectivity.extraInfo).isEqualTo(extraInfo) + assertThat(connectivity.state).isEqualTo(state) + assertThat(connectivity.detailedState).isEqualTo(detailedState) + assertThat(connectivity.type).isEqualTo(type) + assertThat(connectivity.subType).isEqualTo(subType) + assertThat(connectivity.available).isTrue() + assertThat(connectivity.failover).isFalse() + assertThat(connectivity.roaming).isTrue() + assertThat(connectivity.typeName).isEqualTo(typeName) + assertThat(connectivity.subTypeName).isEqualTo(subTypeName) + assertThat(connectivity.reason).isEqualTo(reason) + assertThat(connectivity.extraInfo).isEqualTo(extraInfo) } @Test - fun connectivityShouldNotBeEqualToAnotherOne() { // given - val connectivityOne = Connectivity(state=NetworkInfo.State.CONNECTED, - detailedState=DetailedState.CONNECTED, - type=ConnectivityManager.TYPE_WIFI, - subType=1, - available=true, - failover=true, - roaming=true, - typeName=TYPE_NAME_WIFI, - subTypeName="subtypeOne", - reason="reasonOne", - extraInfo="extraInfoOne") + fun connectivityShouldNotBeEqualToAnotherOne() { + // given + val connectivityOne = Connectivity( + state = NetworkInfo.State.CONNECTED, + detailedState = DetailedState.CONNECTED, + type = ConnectivityManager.TYPE_WIFI, + subType = 1, + available = true, + failover = true, + roaming = true, + typeName = TYPE_NAME_WIFI, + subTypeName = "subtypeOne", + reason = "reasonOne", + extraInfo = "extraInfoOne" + ) - val connectivityTwo = Connectivity(state=NetworkInfo.State.DISCONNECTED, - detailedState=DetailedState.DISCONNECTED, - type=ConnectivityManager.TYPE_MOBILE, - subType=2, - available=false, - failover=false, - roaming=false, - typeName=TYPE_NAME_MOBILE, - subTypeName="subtypeTwo", - reason="reasonTwo", - extraInfo="extraInfoTwo") + val connectivityTwo = Connectivity( + state = NetworkInfo.State.DISCONNECTED, + detailedState = DetailedState.DISCONNECTED, + type = ConnectivityManager.TYPE_MOBILE, + subType = 2, + available = false, + failover = false, + roaming = false, + typeName = TYPE_NAME_MOBILE, + subTypeName = "subtypeTwo", + reason = "reasonTwo", + extraInfo = "extraInfoTwo" + ) // when val isAnotherConnectivityTheSame = connectivityOne == connectivityTwo // then - Truth.assertThat(isAnotherConnectivityTheSame).isFalse() + assertThat(isAnotherConnectivityTheSame).isFalse() } @Test - fun shouldCreateDefaultConnectivityWhenConnectivityManagerIsNull() { // given + fun shouldCreateDefaultConnectivityWhenConnectivityManagerIsNull() { + // given val context = ApplicationProvider.getApplicationContext() val connectivityManager: ConnectivityManager? = null // when val connectivity = create(context, connectivityManager) // then - Truth.assertThat(connectivity.type).isEqualTo(Connectivity.UNKNOWN_TYPE) - Truth.assertThat(connectivity.subType).isEqualTo(Connectivity.UNKNOWN_SUB_TYPE) - Truth.assertThat(connectivity.state) + assertThat(connectivity.type).isEqualTo(Connectivity.UNKNOWN_TYPE) + assertThat(connectivity.subType).isEqualTo(Connectivity.UNKNOWN_SUB_TYPE) + assertThat(connectivity.state) .isEqualTo(NetworkInfo.State.DISCONNECTED) - Truth.assertThat(connectivity.detailedState) + assertThat(connectivity.detailedState) .isEqualTo(DetailedState.IDLE) - Truth.assertThat(connectivity.available).isFalse() - Truth.assertThat(connectivity.failover).isFalse() - Truth.assertThat(connectivity.roaming).isFalse() - Truth.assertThat(connectivity.typeName) + assertThat(connectivity.available).isFalse() + assertThat(connectivity.failover).isFalse() + assertThat(connectivity.roaming).isFalse() + assertThat(connectivity.typeName) .isEqualTo(TYPE_NAME_NONE) - Truth.assertThat(connectivity.subTypeName) + assertThat(connectivity.subTypeName) .isEqualTo(TYPE_NAME_NONE) - Truth.assertThat(connectivity.reason).isEmpty() - Truth.assertThat(connectivity.extraInfo).isEmpty() + assertThat(connectivity.reason).isEmpty() + assertThat(connectivity.extraInfo).isEmpty() } companion object { @@ -280,4 +307,4 @@ class ConnectivityTest { private const val TYPE_NAME_MOBILE = "MOBILE" private const val TYPE_NAME_NONE = "NONE" } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/PreconditionsTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/PreconditionsTest.kt index f4994a8..b6e6395 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/PreconditionsTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/PreconditionsTest.kt @@ -1,6 +1,6 @@ package ru.beryukhov.reactivenetwork -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -17,28 +17,28 @@ class PreconditionsTest { @Config(sdk = [21]) fun shouldBeAtLeastAndroidLollipop() { val isAtLeastAndroidLollipop = isAtLeastAndroidLollipop() - Truth.assertThat(isAtLeastAndroidLollipop).isTrue() + assertThat(isAtLeastAndroidLollipop).isTrue() } @Test @Config(sdk = [22]) fun shouldBeAtLeastAndroidLollipopForHigherApi() { val isAtLeastAndroidLollipop = isAtLeastAndroidLollipop() - Truth.assertThat(isAtLeastAndroidLollipop).isTrue() + assertThat(isAtLeastAndroidLollipop).isTrue() } @Test @Config(sdk = [22]) fun shouldNotBeAtLeastAndroidMarshmallowForLowerApi() { val isAtLeastAndroidMarshmallow = isAtLeastAndroidMarshmallow() - Truth.assertThat(isAtLeastAndroidMarshmallow).isFalse() + assertThat(isAtLeastAndroidMarshmallow).isFalse() } @Test @Config(sdk = [23]) fun shouldBeAtLeastAndroidMarshmallow() { val isAtLeastAndroidMarshmallow = isAtLeastAndroidMarshmallow() - Truth.assertThat(isAtLeastAndroidMarshmallow).isTrue() + assertThat(isAtLeastAndroidMarshmallow).isTrue() } @Test(expected = IllegalArgumentException::class) @@ -118,4 +118,4 @@ class PreconditionsTest { private const val MSG_VALUE_IS_NOT_GREATER_THAN_ZERO = "value is not greater than zero" } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/ReactiveNetworkTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/ReactiveNetworkTest.kt index 63739ad..3bb953b 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/ReactiveNetworkTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/ReactiveNetworkTest.kt @@ -3,16 +3,16 @@ package ru.beryukhov.reactivenetwork import android.content.Context import android.net.NetworkInfo import androidx.test.core.app.ApplicationProvider +import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config -import ru.beryukhov.reactivenetwork.base.BaseFlowTest import ru.beryukhov.reactivenetwork.internet.observing.InternetObservingSettings.Companion.builder import ru.beryukhov.reactivenetwork.internet.observing.InternetObservingStrategy import ru.beryukhov.reactivenetwork.internet.observing.error.DefaultErrorHandler @@ -22,9 +22,10 @@ import ru.beryukhov.reactivenetwork.network.observing.NetworkObservingStrategy import ru.beryukhov.reactivenetwork.network.observing.strategy.LollipopNetworkObservingStrategy @RunWith(RobolectricTestRunner::class) -class ReactiveNetworkTest : BaseFlowTest() { +class ReactiveNetworkTest { @Test - fun testReactiveNetworkObjectShouldNotBeNull() { // given + fun testReactiveNetworkObjectShouldNotBeNull() { + // given // when val reactiveNetwork = ReactiveNetwork() // then @@ -32,13 +33,15 @@ class ReactiveNetworkTest : BaseFlowTest() { } @Test - fun observeNetworkConnectivityShouldNotBeNull() { // given + fun observeNetworkConnectivityShouldNotBeNull() { + // given networkConnectivityObservableShouldNotBeNull() } @Test @Config(sdk = [23]) - fun observeNetworkConnectivityShouldNotBeNullForMarshmallow() { // given + fun observeNetworkConnectivityShouldNotBeNullForMarshmallow() { + // given networkConnectivityObservableShouldNotBeNull() } @@ -48,7 +51,8 @@ class ReactiveNetworkTest : BaseFlowTest() { networkConnectivityObservableShouldNotBeNull() } - private fun networkConnectivityObservableShouldNotBeNull() { // given + private fun networkConnectivityObservableShouldNotBeNull() { + // given val context: Context = ApplicationProvider.getApplicationContext() // when val observable = ReactiveNetwork().observeNetworkConnectivity(context) @@ -57,7 +61,8 @@ class ReactiveNetworkTest : BaseFlowTest() { } @Test - fun observeNetworkConnectivityWithStrategyShouldNotBeNull() { // given + fun observeNetworkConnectivityWithStrategyShouldNotBeNull() { + // given val context: Context = ApplicationProvider.getApplicationContext() val strategy: NetworkObservingStrategy = LollipopNetworkObservingStrategy() // when @@ -67,7 +72,8 @@ class ReactiveNetworkTest : BaseFlowTest() { } @Test - fun observeInternetConnectivityDefaultShouldNotBeNull() { // given + fun observeInternetConnectivityDefaultShouldNotBeNull() { + // given // when val observable = ReactiveNetwork().observeInternetConnectivity() // then @@ -75,20 +81,21 @@ class ReactiveNetworkTest : BaseFlowTest() { } @Test - fun observeNetworkConnectivityShouldBeConnectedOnStartWhenNetworkIsAvailable() { - runBlocking { - // given - val context = ApplicationProvider.getApplicationContext() - // when - val connectivityFlow = - ReactiveNetwork().observeNetworkConnectivity(context).map { it.state } - // then - connectivityFlow.expectFirst(NetworkInfo.State.CONNECTED) + fun observeNetworkConnectivityShouldBeConnectedOnStartWhenNetworkIsAvailable() = runTest { + // given + val context = ApplicationProvider.getApplicationContext() + // when + val connectivityFlow = + ReactiveNetwork().observeNetworkConnectivity(context).map { it.state } + // then + connectivityFlow.test { + assertThat(awaitItem()).isEqualTo(NetworkInfo.State.CONNECTED) } } @Test - fun observeInternetConnectivityShouldNotThrowAnExceptionWhenStrategyIsNotNull() { // given + fun observeInternetConnectivityShouldNotThrowAnExceptionWhenStrategyIsNotNull() { + // given val strategy: InternetObservingStrategy = SocketInternetObservingStrategy() val errorHandler: ErrorHandler = DefaultErrorHandler() @@ -108,7 +115,8 @@ class ReactiveNetworkTest : BaseFlowTest() { } @Test - fun shouldObserveInternetConnectivityWithCustomSettings() { // given + fun shouldObserveInternetConnectivityWithCustomSettings() { + // given val initialInterval = 1 val interval = 2 val host = "www.test.com" @@ -181,4 +189,4 @@ class ReactiveNetworkTest : BaseFlowTest() { private const val TEST_VALID_INITIAL_INTERVAL = 1000 private const val TEST_VALID_HTTP_RESPONSE = 204 } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/BaseFlowTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/BaseFlowTest.kt deleted file mode 100644 index 472cf71..0000000 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/BaseFlowTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ru.beryukhov.reactivenetwork.base - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.launchIn -import org.junit.Rule -import kotlin.test.assertEquals - -abstract class BaseFlowTest(overrideMainDispatcher: Boolean = false) { - @get:Rule - val testScopeRule = TestCoroutineScopeRule(overrideMainDispatcher) - - /*@After - fun cleanup() { - testScopeRule.cleanupTestCoroutines() - }*/ - - suspend fun Flow.expectFirst(expected: T) { - launchIn(testScopeRule) - assertEquals(expected, first()) - } -} \ No newline at end of file diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/FlowExt.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/FlowExt.kt deleted file mode 100644 index 18ca1e1..0000000 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/FlowExt.kt +++ /dev/null @@ -1,26 +0,0 @@ -package ru.beryukhov.reactivenetwork.base - -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.test.TestCoroutineScope - -/** - * Tests a [Flow] by creating and returning a [TestFlow] which caches all value - * emissions, error and completion. - */ -fun Flow.test(): TestFlow = - TestFlow(this) - -/** - * Tests a [Flow] by creating and returning a [TestFlow] which caches all value - * emissions, error and completion. - * - * The [TestFlow] is also launched inside the [scope]. - */ -@OptIn(ExperimentalCoroutinesApi::class) -fun Flow.testIn(scope: TestCoroutineScope): TestFlow { - val testFlow = TestFlow(this) - testFlow.launchIn(scope) - return testFlow -} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestCoroutineScopeRule.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestCoroutineScopeRule.kt deleted file mode 100644 index 3bb985a..0000000 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestCoroutineScopeRule.kt +++ /dev/null @@ -1,38 +0,0 @@ -package ru.beryukhov.reactivenetwork.base - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestCoroutineDispatcher -import kotlinx.coroutines.test.TestCoroutineScope -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.setMain -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import kotlin.coroutines.ContinuationInterceptor - -/** - * Rule that is a [TestCoroutineScope]. - * Coroutine's launched in [TestCoroutineScopeRule] are auto canceled after the test completes. - * - * @property overrideMainDispatcher Boolean if set to true, [Dispatchers.Main] will be overriden - * with [dispatcher]. - */ -@OptIn(ExperimentalCoroutinesApi::class) -class TestCoroutineScopeRule( - val overrideMainDispatcher: Boolean = false -) : TestRule, TestCoroutineScope by TestCoroutineScope() { - - val dispatcher = coroutineContext[ContinuationInterceptor] as TestCoroutineDispatcher - - override fun apply(base: Statement, description: Description): Statement = - object : Statement() { - @Throws(Throwable::class) - override fun evaluate() { - if (overrideMainDispatcher) Dispatchers.setMain(dispatcher) - base.evaluate() - cleanupTestCoroutines() - if (overrideMainDispatcher) Dispatchers.resetMain() - } - } -} \ No newline at end of file diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestFlow.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestFlow.kt deleted file mode 100644 index dca4e8e..0000000 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestFlow.kt +++ /dev/null @@ -1,62 +0,0 @@ -package ru.beryukhov.reactivenetwork.base - -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.flow.AbstractFlow -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.FlowCollector -import kotlinx.coroutines.flow.collect - -/** - * A [Flow] that contains all value emissions, the error and the completion of a [Flow] that is - * tested with [Flow.test] or [Flow.testIn]. - */ -@OptIn(FlowPreview::class) -class TestFlow( - private val source: Flow -) : AbstractFlow() { - - private val mutableEmissions: MutableList = mutableListOf() - - // Tag for this [TestCollector] that is used by the DSL - var tag: String = this::class.java.simpleName - - /** - * All emissions of the collected [Flow]. - */ - val emissions: List = mutableEmissions - - /** - * Error of the collected [Flow]. - */ - var error: Throwable? = null - private set - - /** - * Completion of the collected [Flow]. - */ - var completed: Boolean = false - private set - - @Suppress("TooGenericExceptionCaught") - override suspend fun collectSafely(collector: FlowCollector) { - try { - source.collect { - mutableEmissions.add(it) - collector.emit(it) - } - } catch (throwable: Throwable) { - error = throwable - } finally { - completed = true - } - } - - /** - * Resets [emissions], [error] and [completed]. - */ - fun reset() { - mutableEmissions.clear() - error = null - completed = false - } -} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestFlowExt.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestFlowExt.kt deleted file mode 100644 index f2dc042..0000000 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/base/TestFlowExt.kt +++ /dev/null @@ -1,217 +0,0 @@ -package ru.beryukhov.reactivenetwork.base - -import kotlinx.coroutines.flow.Flow -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNotEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue - -typealias TestFlowExpectation = (TestFlow) -> Unit - -/** - * Expects a certain [TestFlowExpectation] from the [TestFlow]. - */ -infix fun TestFlow.expect(expectation: TestFlowExpectation): TestFlow { - expectation(this) - return this -} - -/** - * Asserts that no errors occurred during collection the the [Flow]. - */ - -fun noError(): TestFlowExpectation<*> = { testFlow -> - assertNull(testFlow.error, "${testFlow.tag} has error") -} - -/** - * Asserts that an errors occurred during collection the the [Flow]. - */ - -fun anyError(): TestFlowExpectation<*> = { testFlow -> - assertNotNull(testFlow.error, "${testFlow.tag} has no error") -} - -/** - * Asserts that an [T] error occurred during collection of the [Flow]. - */ - -inline fun error(): TestFlowExpectation<*> = { testFlow -> - assertNotNull(testFlow.error, "${testFlow.tag} has no error") - assertEquals(T::class, testFlow.error!!::class, "${testFlow.tag} has wrong error") -} - -/** - * Asserts that no emissions occurred during collection the the [Flow]. - */ - -fun noEmissions(): TestFlowExpectation<*> = { testFlow -> - assertEquals(0, testFlow.emissions.count(), "${testFlow.tag} has values") -} - -/** - * Asserts that any emissions occurred during collection the the [Flow]. - */ - -fun anyEmission(): TestFlowExpectation<*> = { testFlow -> - testFlow.assertNotEmpty() -} - -/** - * Asserts that [expected] count of emissions occurred during collection the the [Flow]. - */ - -fun emissionCount(expected: Int): TestFlowExpectation<*> = { testFlow -> - assertEquals( - expected, - testFlow.emissions.count(), - "${testFlow.tag} has wrong emissions count" - ) -} - -/** - * Asserts that [expected] emissions occurred during collection the the [Flow]. - */ - -fun emissions(vararg expected: T): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - assertEquals(expected.toList(), testFlow.emissions, "${testFlow.tag} has wrong emissions") -} - -/** - * Asserts that [expected] emissions occurred during collection the the [Flow]. - */ - -fun emissions(expected: List): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - assertEquals(expected, testFlow.emissions, "${testFlow.tag} has wrong emissions") -} - -/** - * Asserts the [predicate] for every emission that occurred during collection the the [Flow]. - */ - -fun allEmissions(predicate: (T) -> Boolean): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - testFlow.emissions.forEachIndexed { index, emission -> - assertTrue( - predicate.invoke(emission), - "${testFlow.tag} emission at $index does not match predicate" - ) - } -} - -/** - * Asserts that [expected] emission occurred at [index] in the [emissions] collection. - */ - -fun emission(index: Int, expected: T): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - testFlow.hasEmissionAt(index) - assertEquals( - expected, - testFlow.emissions[index], - "${testFlow.tag} emission at $index is not $expected" - ) -} - -/** - * Asserts the [predicate] for the emission at [index] in the [emissions] collection. - */ - -fun emission(index: Int, predicate: (T) -> Boolean): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - testFlow.hasEmissionAt(index) - assertTrue( - predicate.invoke(testFlow.emissions[index]), - "${testFlow.tag} emission at $index does not match predicate" - ) -} - -/** - * Asserts that [expected] emission occurred first in the [emissions] collection. - */ - -fun firstEmission(expected: T): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - assertEquals(expected, testFlow.emissions.first(), "${testFlow.tag} has wrong first emission") -} - -/** - * Asserts the [predicate] for the first emission in the [emissions] collection. - */ - -fun firstEmission(predicate: (T) -> Boolean): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - assertTrue( - predicate.invoke(testFlow.emissions.first()), - "${testFlow.tag} first emission does not match predicate" - ) -} - -/** - * Asserts that [expected] emission occurred last in the [emissions] collection. - */ - -fun lastEmission(expected: T): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - assertEquals(expected, testFlow.emissions.last(), "${testFlow.tag} has wrong last emission") -} - -/** - * Asserts the [predicate] for the last emission in the [emissions] collection. - */ - -fun lastEmission(predicate: (T) -> Boolean): TestFlowExpectation = { testFlow -> - testFlow.assertNotEmpty() - assertTrue( - predicate.invoke(testFlow.emissions.last()), - "${testFlow.tag} has wrong last emission matching predicate" - ) -} - -/** - * Asserts that the [TestFlow] has not completed emission collection. - */ - -fun noCompletion(): TestFlowExpectation<*> = { testFlow -> - assertFalse(testFlow.completed, "${testFlow.tag} has completion") -} - -/** - * Asserts that the [TestFlow] has completed emission collection. - */ - -fun anyCompletion(): TestFlowExpectation<*> = { testFlow -> - assertTrue(testFlow.completed, "${testFlow.tag} has no completion") -} - -/** - * Asserts that the [TestFlow] has completed emission collection with no error. - */ - -fun regularCompletion(): TestFlowExpectation<*> = { testFlow -> - testFlow expect anyCompletion() - testFlow expect noError() -} - -/** - * Asserts that the [TestFlow] has completed emission collection with an error of type [T]. - */ - -inline fun exceptionalCompletion(): TestFlowExpectation<*> = { testFlow -> - testFlow expect anyCompletion() - testFlow expect error() -} - - -private fun TestFlow<*>.assertNotEmpty() { - assertNotEquals(0, emissions.count(), "$tag has no values") -} - - -private fun TestFlow<*>.hasEmissionAt(index: Int) { - assertNotNull(emissions.getOrNull(index), "$tag has no emission at $index") -} \ No newline at end of file diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingSettingsTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingSettingsTest.kt index f73423e..4a6ba84 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingSettingsTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/InternetObservingSettingsTest.kt @@ -1,6 +1,6 @@ package ru.beryukhov.reactivenetwork.internet.observing -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -17,27 +17,28 @@ class InternetObservingSettingsTest { fun shouldCreateSettings() { // when val settings = create() // then - Truth.assertThat(settings).isNotNull() + assertThat(settings).isNotNull() } @Test fun shouldBuildSettingsWithDefaultValues() { // when val settings = create() // then - Truth.assertThat(settings.initialInterval()).isEqualTo(0) - Truth.assertThat(settings.interval()).isEqualTo(2000) - Truth.assertThat(settings.host()).isEqualTo("http://clients3.google.com/generate_204") - Truth.assertThat(settings.port()).isEqualTo(80) - Truth.assertThat(settings.timeout()).isEqualTo(2000) - Truth.assertThat(settings.httpResponse()).isEqualTo(204) - Truth.assertThat(settings.errorHandler()) + assertThat(settings.initialInterval()).isEqualTo(0) + assertThat(settings.interval()).isEqualTo(2000) + assertThat(settings.host()).isEqualTo("http://clients3.google.com/generate_204") + assertThat(settings.port()).isEqualTo(80) + assertThat(settings.timeout()).isEqualTo(2000) + assertThat(settings.httpResponse()).isEqualTo(204) + assertThat(settings.errorHandler()) .isInstanceOf(DefaultErrorHandler::class.java) - Truth.assertThat(settings.strategy()) + assertThat(settings.strategy()) .isInstanceOf(WalledGardenInternetObservingStrategy::class.java) } @Test - fun shouldBuildSettings() { // given + fun shouldBuildSettings() { + // given val initialInterval = 1 val interval = 2 val host = "www.test.com" @@ -59,16 +60,16 @@ class InternetObservingSettingsTest { .strategy(strategy) .build() // then - Truth.assertThat(settings.initialInterval()).isEqualTo(initialInterval) - Truth.assertThat(settings.interval()).isEqualTo(interval) - Truth.assertThat(settings.host()).isEqualTo(host) - Truth.assertThat(settings.port()).isEqualTo(port) - Truth.assertThat(settings.timeout()).isEqualTo(timeout) - Truth.assertThat(settings.httpResponse()).isEqualTo(httpResponse) - Truth.assertThat(settings.errorHandler()).isNotNull() - Truth.assertThat(settings.errorHandler()) + assertThat(settings.initialInterval()).isEqualTo(initialInterval) + assertThat(settings.interval()).isEqualTo(interval) + assertThat(settings.host()).isEqualTo(host) + assertThat(settings.port()).isEqualTo(port) + assertThat(settings.timeout()).isEqualTo(timeout) + assertThat(settings.httpResponse()).isEqualTo(httpResponse) + assertThat(settings.errorHandler()).isNotNull() + assertThat(settings.errorHandler()) .isNotInstanceOf(DefaultErrorHandler::class.java) - Truth.assertThat(settings.strategy()) + assertThat(settings.strategy()) .isInstanceOf(SocketInternetObservingStrategy::class.java) } @@ -81,4 +82,4 @@ class InternetObservingSettingsTest { } } } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/DefaultErrorHandlerTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/DefaultErrorHandlerTest.kt index 83ba322..b1a9640 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/DefaultErrorHandlerTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/error/DefaultErrorHandlerTest.kt @@ -4,7 +4,6 @@ import io.mockk.spyk import io.mockk.verify import org.junit.Test import org.junit.runner.RunWith - import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) @@ -13,12 +12,13 @@ open class DefaultErrorHandlerTest { private val handler = spyk(DefaultErrorHandler()) @Test - fun shouldHandleErrorDuringClosingSocket() { // given + fun shouldHandleErrorDuringClosingSocket() { + // given val errorMsg = "Could not close the socket" val exception = Exception(errorMsg) // when handler.handleError(exception, errorMsg) // then - verify(exactly = 1){handler.handleError(exception, errorMsg)} + verify(exactly = 1) { handler.handleError(exception, errorMsg) } } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/SocketInternetObservingStrategyTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/SocketInternetObservingStrategyTest.kt index 8348fcb..6a689c2 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/SocketInternetObservingStrategyTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/SocketInternetObservingStrategyTest.kt @@ -1,26 +1,22 @@ package ru.beryukhov.reactivenetwork.internet.observing.strategy -import com.google.common.truth.Truth +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat import io.mockk.every import io.mockk.mockk import io.mockk.spyk import io.mockk.verify -import java.io.IOException -import java.net.InetSocketAddress -import java.net.Socket -import kotlinx.coroutines.runBlocking -import org.junit.Ignore +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import ru.beryukhov.reactivenetwork.base.BaseFlowTest -import ru.beryukhov.reactivenetwork.base.emission -import ru.beryukhov.reactivenetwork.base.expect -import ru.beryukhov.reactivenetwork.base.testIn import ru.beryukhov.reactivenetwork.internet.observing.error.ErrorHandler +import java.io.IOException +import java.net.InetSocketAddress +import java.net.Socket @RunWith(RobolectricTestRunner::class) -class SocketInternetObservingStrategyTest : BaseFlowTest() { +class SocketInternetObservingStrategyTest { private val strategy = spyk(SocketInternetObservingStrategy()) private val errorHandler = mockk(relaxed = true) @@ -28,149 +24,147 @@ class SocketInternetObservingStrategyTest : BaseFlowTest() { private val host: String = strategy.getDefaultPingHost() - @Ignore @Test - fun shouldBeConnectedToTheInternet() { // given + fun shouldBeConnectedToTheInternet() = runTest { + // given every { strategy.isConnected( - host, - PORT, - TIMEOUT_IN_MS, - errorHandler + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + errorHandler = errorHandler ) } returns true // when - val testFlow = strategy.observeInternetConnectivity( - INITIAL_INTERVAL_IN_MS, - INTERVAL_IN_MS, - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandler - ).testIn(scope = testScopeRule) - - // then - testFlow expect emission(index = 0, expected = true) - + strategy.observeInternetConnectivity( + initialIntervalInMs = INITIAL_INTERVAL_IN_MS, + intervalInMs = INTERVAL_IN_MS, + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + httpResponse = HTTP_RESPONSE, + errorHandler = errorHandler + ).test { + // then + assertThat(awaitItem()).isEqualTo(true) + } } - @Ignore @Test - fun shouldNotBeConnectedToTheInternet() { // given + fun shouldNotBeConnectedToTheInternet() = runTest { + // given every { strategy.isConnected( - host, - PORT, - TIMEOUT_IN_MS, - errorHandler + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + errorHandler = errorHandler ) } returns false // when - val testFlow = strategy.observeInternetConnectivity( - INITIAL_INTERVAL_IN_MS, - INTERVAL_IN_MS, - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandler - ).testIn(scope = testScopeRule) - - // then - testFlow expect emission(index = 0, expected = false) - + strategy.observeInternetConnectivity( + initialIntervalInMs = INITIAL_INTERVAL_IN_MS, + intervalInMs = INTERVAL_IN_MS, + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + httpResponse = HTTP_RESPONSE, + errorHandler = errorHandler + ).test { + // then + assertThat(awaitItem()).isEqualTo(false) + } } @Test @Throws(IOException::class) - fun shouldNotBeConnectedToTheInternetWhenSocketThrowsAnExceptionOnConnect() { // given + fun shouldNotBeConnectedToTheInternetWhenSocketThrowsAnExceptionOnConnect() { + // given val address = InetSocketAddress( host, PORT ) - every { socket.connect(address, TIMEOUT_IN_MS) } throws (IOException()) + every { socket.connect(address, TIMEOUT_IN_MS) } throws IOException() // when val isConnected = strategy.isConnected( - socket, - host, - PORT, - TIMEOUT_IN_MS, - errorHandler + socket = socket, + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + errorHandler = errorHandler ) // then - Truth.assertThat(isConnected).isFalse() + assertThat(isConnected).isFalse() } @Test @Throws(IOException::class) - fun shouldHandleAnExceptionThrownDuringClosingTheSocket() { // given + fun shouldHandleAnExceptionThrownDuringClosingTheSocket() { + // given val errorMsg = "Could not close the socket" val givenException = IOException(errorMsg) - every { socket.close() } throws (givenException) + every { socket.close() } throws givenException // when strategy.isConnected( - socket, - host, - PORT, - TIMEOUT_IN_MS, - errorHandler + socket = socket, + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + errorHandler = errorHandler ) // then verify(exactly = 1) { errorHandler.handleError(givenException, errorMsg) } } @Test - fun shouldBeConnectedToTheInternetViaSingle() { // given + fun shouldBeConnectedToTheInternetViaSingle() = runTest { + // given every { strategy.isConnected( - host, - PORT, - TIMEOUT_IN_MS, - errorHandler + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + errorHandler = errorHandler ) } returns true - runBlocking { - // when - val isConnected = strategy.checkInternetConnectivity( - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandler - ) - // then - Truth.assertThat(isConnected).isTrue() - } + // when + val isConnected = strategy.checkInternetConnectivity( + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + httpResponse = HTTP_RESPONSE, + errorHandler = errorHandler + ) + // then + assertThat(isConnected).isTrue() } @Test - fun shouldNotBeConnectedToTheInternetViaSingle() { // given + fun shouldNotBeConnectedToTheInternetViaSingle() = runTest { + // given every { strategy.isConnected( - host, - PORT, - TIMEOUT_IN_MS, - errorHandler + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + errorHandler = errorHandler ) } returns false - runBlocking { - // when - val isConnected = strategy.checkInternetConnectivity( - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandler - ) - // then - Truth.assertThat(isConnected).isFalse() - } + // when + val isConnected = strategy.checkInternetConnectivity( + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + httpResponse = HTTP_RESPONSE, + errorHandler = errorHandler + ) + // then + assertThat(isConnected).isFalse() } @Test @@ -178,7 +172,7 @@ class SocketInternetObservingStrategyTest : BaseFlowTest() { val transformedHost = strategy.adjustHost(HOST_WITHOUT_HTTP) // then - Truth.assertThat(transformedHost) + assertThat(transformedHost) .isEqualTo(HOST_WITHOUT_HTTP) } @@ -187,7 +181,7 @@ class SocketInternetObservingStrategyTest : BaseFlowTest() { val transformedHost = strategy.adjustHost(HOST_WITH_HTTP) // then - Truth.assertThat(transformedHost) + assertThat(transformedHost) .isEqualTo(HOST_WITHOUT_HTTP) } @@ -196,36 +190,38 @@ class SocketInternetObservingStrategyTest : BaseFlowTest() { val transformedHost = strategy.adjustHost(HOST_WITH_HTTP) // then - Truth.assertThat(transformedHost) + assertThat(transformedHost) .isEqualTo(HOST_WITHOUT_HTTP) } @Test - fun shouldAdjustHostDuringCheckingConnectivity() { // given + fun shouldAdjustHostDuringCheckingConnectivity() = runTest { + // given val host = host every { strategy.isConnected( - host, - PORT, - TIMEOUT_IN_MS, - errorHandler + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + errorHandler = errorHandler ) } returns true // when strategy.observeInternetConnectivity( - INITIAL_INTERVAL_IN_MS, - INTERVAL_IN_MS, - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandler - ).testIn(scope = testScopeRule) + initialIntervalInMs = INITIAL_INTERVAL_IN_MS, + intervalInMs = INTERVAL_IN_MS, + host = host, + port = PORT, + timeoutInMs = TIMEOUT_IN_MS, + httpResponse = HTTP_RESPONSE, + errorHandler = errorHandler + ).test { + cancelAndConsumeRemainingEvents() + } // then verify { strategy.adjustHost(host) } - } companion object { @@ -237,4 +233,4 @@ class SocketInternetObservingStrategyTest : BaseFlowTest() { private const val HOST_WITH_HTTP = "http://www.website.com" private const val HOST_WITHOUT_HTTP = "www.website.com" } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/WalledGardenInternetObservingStrategyTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/WalledGardenInternetObservingStrategyTest.kt index ba73568..58b107f 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/WalledGardenInternetObservingStrategyTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/internet/observing/strategy/WalledGardenInternetObservingStrategyTest.kt @@ -1,26 +1,21 @@ package ru.beryukhov.reactivenetwork.internet.observing.strategy -import com.google.common.truth.Truth +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat import io.mockk.every import io.mockk.mockk import io.mockk.spyk import io.mockk.verify -import java.io.IOException -import java.net.HttpURLConnection -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config -import ru.beryukhov.reactivenetwork.base.BaseFlowTest -import ru.beryukhov.reactivenetwork.base.testIn import ru.beryukhov.reactivenetwork.internet.observing.error.ErrorHandler +import java.io.IOException +import java.net.HttpURLConnection -@Config(manifest = Config.NONE) -@RunWith( - RobolectricTestRunner::class -) -class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { +@RunWith(RobolectricTestRunner::class) +class WalledGardenInternetObservingStrategyTest { private val errorHandler = mockk(relaxed = true) private val strategy = spyk(WalledGardenInternetObservingStrategy()) @@ -28,7 +23,8 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { private val host: String = strategy.getDefaultPingHost() @Test - fun shouldBeConnectedToTheInternet() { // given + fun shouldBeConnectedToTheInternet() = runTest { + // given val errorHandlerStub = createErrorHandlerStub() every { strategy.isConnected( @@ -41,24 +37,23 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { } returns true // when - runBlocking { - val testFlow = strategy.observeInternetConnectivity( - INITIAL_INTERVAL_IN_MS, - INTERVAL_IN_MS, - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandlerStub - ) - + strategy.observeInternetConnectivity( + INITIAL_INTERVAL_IN_MS, + INTERVAL_IN_MS, + host, + PORT, + TIMEOUT_IN_MS, + HTTP_RESPONSE, + errorHandlerStub + ).test { // then - testFlow.expectFirst(true) + assertThat(awaitItem()).isEqualTo(true) } } @Test - fun shouldNotBeConnectedToTheInternet() { // given + fun shouldNotBeConnectedToTheInternet() = runTest { + // given val errorHandlerStub = createErrorHandlerStub() every { @@ -71,24 +66,24 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { ) } returns false // when - runBlocking { - val testFlow = strategy.observeInternetConnectivity( - INITIAL_INTERVAL_IN_MS, - INTERVAL_IN_MS, - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandlerStub - ) + strategy.observeInternetConnectivity( + INITIAL_INTERVAL_IN_MS, + INTERVAL_IN_MS, + host, + PORT, + TIMEOUT_IN_MS, + HTTP_RESPONSE, + errorHandlerStub + ).test { // then - testFlow.expectFirst(false) + assertThat(awaitItem()).isEqualTo(false) } } @Test - fun shouldBeConnectedToTheInternetViaSingle() { // given + fun shouldBeConnectedToTheInternetViaSingle() = runTest { + // given val errorHandlerStub = createErrorHandlerStub() every { strategy.isConnected( @@ -100,22 +95,21 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { ) } returns true - runBlocking { - // when - val isConnected = strategy.checkInternetConnectivity( - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandlerStub - ) - // then - Truth.assertThat(isConnected).isTrue() - } + // when + val isConnected = strategy.checkInternetConnectivity( + host, + PORT, + TIMEOUT_IN_MS, + HTTP_RESPONSE, + errorHandlerStub + ) + // then + assertThat(isConnected).isTrue() } @Test - fun shouldNotBeConnectedToTheInternetViaSingle() { // given + fun shouldNotBeConnectedToTheInternetViaSingle() = runTest { + // given val errorHandlerStub = createErrorHandlerStub() every { @@ -127,23 +121,22 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { errorHandlerStub ) } returns false - runBlocking { - // when - val isConnected = strategy.checkInternetConnectivity( - host, - PORT, - TIMEOUT_IN_MS, - HTTP_RESPONSE, - errorHandlerStub - ) - // then - Truth.assertThat(isConnected).isFalse() - } + // when + val isConnected = strategy.checkInternetConnectivity( + host, + PORT, + TIMEOUT_IN_MS, + HTTP_RESPONSE, + errorHandlerStub + ) + // then + assertThat(isConnected).isFalse() } @Test @Throws(IOException::class) - fun shouldCreateHttpUrlConnection() { // given + fun shouldCreateHttpUrlConnection() { + // given val parsedDefaultHost = "clients3.google.com" // when val connection = strategy.createHttpUrlConnection( @@ -152,21 +145,19 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { TIMEOUT_IN_MS ) // then - Truth.assertThat(connection).isNotNull() - Truth.assertThat(connection.url.host).isEqualTo(parsedDefaultHost) - Truth.assertThat(connection.url.port) - .isEqualTo(PORT) - Truth.assertThat(connection.connectTimeout) - .isEqualTo(TIMEOUT_IN_MS) - Truth.assertThat(connection.readTimeout) - .isEqualTo(TIMEOUT_IN_MS) - Truth.assertThat(connection.instanceFollowRedirects).isFalse() - Truth.assertThat(connection.useCaches).isFalse() + assertThat(connection).isNotNull() + assertThat(connection.url.host).isEqualTo(parsedDefaultHost) + assertThat(connection.url.port).isEqualTo(PORT) + assertThat(connection.connectTimeout).isEqualTo(TIMEOUT_IN_MS) + assertThat(connection.readTimeout).isEqualTo(TIMEOUT_IN_MS) + assertThat(connection.instanceFollowRedirects).isFalse() + assertThat(connection.useCaches).isFalse() } @Test @Throws(IOException::class) - fun shouldHandleAnExceptionWhileCreatingHttpUrlConnection() { // given + fun shouldHandleAnExceptionWhileCreatingHttpUrlConnection() { + // given val errorMsg = "Could not establish connection with WalledGardenStrategy" val givenException = IOException(errorMsg) every { @@ -175,7 +166,7 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { PORT, TIMEOUT_IN_MS ) - } throws (givenException) + } throws givenException // when strategy.isConnected( HOST_WITH_HTTP, @@ -190,7 +181,8 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { @Test @Throws(IOException::class) - fun shouldCreateHttpsUrlConnection() { // given + fun shouldCreateHttpsUrlConnection() { + // given val parsedDefaultHost = "clients3.google.com" // when val connection: HttpURLConnection = strategy.createHttpsUrlConnection( @@ -199,21 +191,19 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { TIMEOUT_IN_MS ) // then - Truth.assertThat(connection).isNotNull() - Truth.assertThat(connection.url.host).isEqualTo(parsedDefaultHost) - Truth.assertThat(connection.url.port) - .isEqualTo(PORT) - Truth.assertThat(connection.connectTimeout) - .isEqualTo(TIMEOUT_IN_MS) - Truth.assertThat(connection.readTimeout) - .isEqualTo(TIMEOUT_IN_MS) - Truth.assertThat(connection.instanceFollowRedirects).isFalse() - Truth.assertThat(connection.useCaches).isFalse() + assertThat(connection).isNotNull() + assertThat(connection.url.host).isEqualTo(parsedDefaultHost) + assertThat(connection.url.port).isEqualTo(PORT) + assertThat(connection.connectTimeout).isEqualTo(TIMEOUT_IN_MS) + assertThat(connection.readTimeout).isEqualTo(TIMEOUT_IN_MS) + assertThat(connection.instanceFollowRedirects).isFalse() + assertThat(connection.useCaches).isFalse() } @Test @Throws(IOException::class) - fun shouldHandleAnExceptionWhileCreatingHttpsUrlConnection() { // given + fun shouldHandleAnExceptionWhileCreatingHttpsUrlConnection() { + // given val errorMsg = "Could not establish connection with WalledGardenStrategy" val givenException = IOException(errorMsg) val host = "https://clients3.google.com" @@ -223,7 +213,7 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { PORT, TIMEOUT_IN_MS ) - } throws (givenException) + } throws givenException // when strategy.isConnected( host, @@ -240,14 +230,14 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { fun shouldNotTransformHttpHost() { // when val transformedHost = strategy.adjustHost(HOST_WITH_HTTPS) // then - Truth.assertThat(transformedHost).isEqualTo(HOST_WITH_HTTPS) + assertThat(transformedHost).isEqualTo(HOST_WITH_HTTPS) } @Test fun shouldNotTransformHttpsHost() { // when val transformedHost = strategy.adjustHost(HOST_WITH_HTTPS) // then - Truth.assertThat(transformedHost) + assertThat(transformedHost) .isEqualTo(HOST_WITH_HTTPS) } @@ -255,11 +245,12 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { fun shouldAddHttpsProtocolToHost() { // when val transformedHost = strategy.adjustHost(HOST_WITHOUT_HTTPS) // then - Truth.assertThat(transformedHost).isEqualTo(HOST_WITH_HTTPS) + assertThat(transformedHost).isEqualTo(HOST_WITH_HTTPS) } @Test - fun shouldAdjustHostWhileCheckingConnectivity() { // given + fun shouldAdjustHostWhileCheckingConnectivity() = runTest { + // given val errorHandlerStub = createErrorHandlerStub() val host = host @@ -283,10 +274,11 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { TIMEOUT_IN_MS, HTTP_RESPONSE, errorHandlerStub - ).testIn(scope = testScopeRule) + ).test { + cancelAndConsumeRemainingEvents() + } // then verify { strategy.adjustHost(host) } - } private fun createErrorHandlerStub(): ErrorHandler { @@ -309,4 +301,4 @@ class WalledGardenInternetObservingStrategyTest : BaseFlowTest() { private const val HOST_WITH_HTTPS = "https://www.website.com" private const val HOST_WITHOUT_HTTPS = "www.website.com" } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/NetworkObservingStrategyTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/NetworkObservingStrategyTest.kt index ec91a26..09c42b5 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/NetworkObservingStrategyTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/NetworkObservingStrategyTest.kt @@ -3,43 +3,41 @@ package ru.beryukhov.reactivenetwork.network.observing import android.content.Context import android.net.NetworkInfo import androidx.test.core.app.ApplicationProvider -import ru.beryukhov.reactivenetwork.base.emission -import ru.beryukhov.reactivenetwork.base.expect -import ru.beryukhov.reactivenetwork.base.testIn +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.map -import org.junit.Ignore +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import ru.beryukhov.reactivenetwork.base.BaseFlowTest import ru.beryukhov.reactivenetwork.network.observing.strategy.LollipopNetworkObservingStrategy import ru.beryukhov.reactivenetwork.network.observing.strategy.PreLollipopNetworkObservingStrategy @RunWith(RobolectricTestRunner::class) -class NetworkObservingStrategyTest: BaseFlowTest() { +class NetworkObservingStrategyTest { @Test - fun lollipopObserveNetworkConnectivityShouldBeConnectedWhenNetworkIsAvailable() { // given + fun lollipopObserveNetworkConnectivityShouldBeConnectedWhenNetworkIsAvailable() { + // given val strategy: NetworkObservingStrategy = LollipopNetworkObservingStrategy() // when assertThatIsConnected(strategy) } - @Ignore @Test - fun preLollipopObserveNetworkConnectivityShouldBeConnectedWhenNetworkIsAvailable() { // given + fun preLollipopObserveNetworkConnectivityShouldBeConnectedWhenNetworkIsAvailable() { + // given val strategy: NetworkObservingStrategy = PreLollipopNetworkObservingStrategy() // when assertThatIsConnected(strategy) } - private fun assertThatIsConnected(strategy: NetworkObservingStrategy) { // given + private fun assertThatIsConnected(strategy: NetworkObservingStrategy) = runTest { + // given val context = ApplicationProvider.getApplicationContext() - //when - - val testFlow = strategy.observeNetworkConnectivity(context).map { it.state }.testIn(scope = testScopeRule) - + // when + strategy.observeNetworkConnectivity(context).map { it.state }.test { // then - testFlow expect emission(index = 0, expected = NetworkInfo.State.CONNECTED) - + assertThat(awaitItem()).isEqualTo(NetworkInfo.State.CONNECTED) + } } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/LollipopNetworkObservingStrategyTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/LollipopNetworkObservingStrategyTest.kt index 83a8519..d8e80fa 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/LollipopNetworkObservingStrategyTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/LollipopNetworkObservingStrategyTest.kt @@ -3,50 +3,34 @@ package ru.beryukhov.reactivenetwork.network.observing.strategy import android.content.Context import android.net.NetworkInfo import androidx.test.core.app.ApplicationProvider -import ru.beryukhov.reactivenetwork.base.emission -import ru.beryukhov.reactivenetwork.base.expect -import ru.beryukhov.reactivenetwork.base.testIn +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat import io.mockk.spyk import io.mockk.verify import kotlinx.coroutines.flow.map +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import ru.beryukhov.reactivenetwork.base.BaseFlowTest import ru.beryukhov.reactivenetwork.network.observing.NetworkObservingStrategy @RunWith(RobolectricTestRunner::class) -class LollipopNetworkObservingStrategyTest : BaseFlowTest() { +class LollipopNetworkObservingStrategyTest { @Test - fun shouldObserveConnectivity() { // given + fun shouldObserveConnectivity() = runTest { + // given val strategy: NetworkObservingStrategy = LollipopNetworkObservingStrategy() val context = ApplicationProvider.getApplicationContext() - // when - - val testFlow = strategy.observeNetworkConnectivity(context).map { it.state } - .testIn(scope = testScopeRule) - // then - testFlow expect emission(index = 0, expected = NetworkInfo.State.CONNECTED) + strategy.observeNetworkConnectivity(context).map { it.state }.test { + assertThat(awaitItem()).isEqualTo(NetworkInfo.State.CONNECTED) + } } - //Rx specific - /*@Test - fun shouldStopObservingConnectivity() { // given - val strategy: NetworkObservingStrategy = LollipopNetworkObservingStrategy() - val context = ApplicationProvider.getApplicationContext() - val observable: Observable = strategy.observeNetworkConnectivity(context) - val observer: TestObserver = TestObserver() - // when - observable.subscribe(observer) - observer.dispose() - // then - assertThat(observer.isDisposed()).isTrue() - }*/ - @Test - fun shouldCallOnError() { // given + fun shouldCallOnError() { + // given val message = "error message" val exception = Exception() val strategy = spyk(LollipopNetworkObservingStrategy()) @@ -55,4 +39,4 @@ class LollipopNetworkObservingStrategyTest : BaseFlowTest() { // then verify(exactly = 1) { strategy.onError(message, exception) } } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/MarshmallowNetworkObservingStrategyTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/MarshmallowNetworkObservingStrategyTest.kt index c580eb5..e72d3c7 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/MarshmallowNetworkObservingStrategyTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/MarshmallowNetworkObservingStrategyTest.kt @@ -10,31 +10,22 @@ import android.net.NetworkInfo import android.os.Build import android.os.PowerManager import androidx.test.core.app.ApplicationProvider -import com.google.common.truth.Truth +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat import io.mockk.every import io.mockk.mockk import io.mockk.spyk import io.mockk.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.map -import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.runTest import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith - import org.robolectric.RobolectricTestRunner -import ru.beryukhov.reactivenetwork.base.BaseFlowTest import ru.beryukhov.reactivenetwork.Connectivity -import ru.beryukhov.reactivenetwork.base.emission -import ru.beryukhov.reactivenetwork.base.emissionCount -import ru.beryukhov.reactivenetwork.base.emissions -import ru.beryukhov.reactivenetwork.base.expect -import ru.beryukhov.reactivenetwork.base.testIn @RunWith(RobolectricTestRunner::class) -open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { +open class MarshmallowNetworkObservingStrategyTest { private val strategy = spyk(MarshmallowNetworkObservingStrategy()) @@ -52,28 +43,19 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { } @Test - fun shouldObserveConnectivity() { // given + fun shouldObserveConnectivity() = runTest { + // given val context = ApplicationProvider.getApplicationContext() - // when val testFlow = strategy.observeNetworkConnectivity(context).map { it.state } - .testIn(scope = testScopeRule) - // then - testFlow expect emission(index = 0, expected = NetworkInfo.State.CONNECTED) + .test { + assertThat(awaitItem()).isEqualTo(NetworkInfo.State.CONNECTED) + } } - //Rx specific test - /*@Test - fun shouldStopObservingConnectivity() { // given - val observable: Observable = strategy.observeNetworkConnectivity(context!!) - // when - val disposable: Disposable = observable.subscribe() - disposable.dispose() - // then - assertThat(disposable.isDisposed()).isTrue() - }*/ @Test - fun shouldCallOnError() { // given + fun shouldCallOnError() { + // given val message = "error message" val exception = Exception() // when @@ -82,72 +64,73 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { verify(exactly = 1) { strategy.onError(message, exception) } } - @OptIn(ExperimentalCoroutinesApi::class) - @Ignore @Test - fun shouldTryToUnregisterCallbackOnDispose() { // given + fun shouldTryToUnregisterCallbackOnDispose() = runTest { + // given // when - runBlockingTest { - val testFlow = strategy.observeNetworkConnectivity(context).testIn(scope = this) - this.cancel() - - // then - verify { strategy.tryToUnregisterCallback(any()) } + strategy.observeNetworkConnectivity(context).test { + cancelAndConsumeRemainingEvents() } + + // then + verify { strategy.tryToUnregisterCallback(any()) } } - @OptIn(ExperimentalCoroutinesApi::class) - @Ignore @Test - fun shouldTryToUnregisterReceiverOnDispose() { // given + fun shouldTryToUnregisterReceiverOnDispose() = runTest { + // given // when - runBlockingTest { - val testFlow = strategy.observeNetworkConnectivity(context).testIn(scope = this) - this.cancel() - - // then - verify { strategy.tryToUnregisterReceiver(context) } + strategy.observeNetworkConnectivity(context).test { + cancelAndConsumeRemainingEvents() } + + // then + verify { strategy.tryToUnregisterReceiver(context) } } @Test - fun shouldNotBeInIdleModeWhenDeviceIsNotInIdleAndIsNotIgnoringBatteryOptimizations() { // given + fun shouldNotBeInIdleModeWhenDeviceIsNotInIdleAndIsNotIgnoringBatteryOptimizations() { + // given preparePowerManagerMocks(idleMode = false, ignoreOptimizations = false) // when val isIdleMode = strategy.isIdleMode(contextMock) // then - Truth.assertThat(isIdleMode).isFalse() + assertThat(isIdleMode).isFalse() } @Test - fun shouldBeInIdleModeWhenDeviceIsNotIgnoringBatteryOptimizations() { // given + fun shouldBeInIdleModeWhenDeviceIsNotIgnoringBatteryOptimizations() { + // given preparePowerManagerMocks(idleMode = true, ignoreOptimizations = false) // when val isIdleMode = strategy.isIdleMode(contextMock) // then - Truth.assertThat(isIdleMode).isTrue() + assertThat(isIdleMode).isTrue() } @Test - fun shouldNotBeInIdleModeWhenDeviceIsInIdleModeAndIgnoringBatteryOptimizations() { // given + fun shouldNotBeInIdleModeWhenDeviceIsInIdleModeAndIgnoringBatteryOptimizations() { + // given preparePowerManagerMocks(idleMode = true, ignoreOptimizations = true) // when val isIdleMode = strategy.isIdleMode(contextMock) // then - Truth.assertThat(isIdleMode).isFalse() + assertThat(isIdleMode).isFalse() } @Test - fun shouldNotBeInIdleModeWhenDeviceIsNotInIdleMode() { // given + fun shouldNotBeInIdleModeWhenDeviceIsNotInIdleMode() { + // given preparePowerManagerMocks(idleMode = false, ignoreOptimizations = true) // when val isIdleMode = strategy.isIdleMode(contextMock) // then - Truth.assertThat(isIdleMode).isFalse() + assertThat(isIdleMode).isFalse() } @Test - fun shouldReceiveIntentInIdleMode() { // given + fun shouldReceiveIntentInIdleMode() { + // given preparePowerManagerMocks(idleMode = true, ignoreOptimizations = false) val broadcastReceiver = strategy.createIdleBroadcastReceiver() // when @@ -155,12 +138,13 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { // then verify { strategy.onNext(any()) } } - - @Ignore @Test - fun shouldReceiveIntentWhenIsNotInIdleMode() { // given + fun shouldReceiveIntentWhenIsNotInIdleMode() { + // given preparePowerManagerMocks(idleMode = false, ignoreOptimizations = false) val broadcastReceiver = strategy.createIdleBroadcastReceiver() + every { contextMock.getSystemService(Context.CONNECTIVITY_SERVICE) } returns connectivityManager + every { connectivityManager.activeNetworkInfo } returns null // when broadcastReceiver.onReceive(contextMock, intent) // then @@ -180,9 +164,11 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { } @Test - fun shouldCreateNetworkCallbackOnSubscribe() { + fun shouldCreateNetworkCallbackOnSubscribe() = runTest { // when - val testFlow = strategy.observeNetworkConnectivity(context).testIn(scope = testScopeRule) + strategy.observeNetworkConnectivity(context).test { + cancelAndConsumeRemainingEvents() + } // then verify { strategy.createNetworkCallback(context) } @@ -190,7 +176,8 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Test - fun shouldInvokeOnNextOnNetworkAvailable() { // given + fun shouldInvokeOnNextOnNetworkAvailable() { + // given val networkCallback = strategy.createNetworkCallback(context) // when networkCallback.onAvailable(network) @@ -200,7 +187,8 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Test - fun shouldInvokeOnNextOnNetworkLost() { // given + fun shouldInvokeOnNextOnNetworkLost() { + // given val networkCallback = strategy.createNetworkCallback(context) // when networkCallback.onLost(network) @@ -210,7 +198,8 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Test - fun shouldHandleErrorWhileTryingToUnregisterCallback() { // given + fun shouldHandleErrorWhileTryingToUnregisterCallback() { + // given strategy.observeNetworkConnectivity(context) val exception = IllegalArgumentException() every { connectivityManager.unregisterNetworkCallback(any()) } throws exception @@ -226,7 +215,8 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { } @Test - fun shouldHandleErrorWhileTryingToUnregisterReceiver() { // given + fun shouldHandleErrorWhileTryingToUnregisterReceiver() { + // given strategy.observeNetworkConnectivity(context) val exception = RuntimeException() every { contextMock.unregisterReceiver(any()) } throws exception @@ -255,9 +245,8 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { assertThatConnectivityIsPropagatedDuringChange(lastType, currentType) } - private fun assertThatConnectivityIsPropagatedDuringChange( - lastType: Int, currentType: Int - ) { // given + private fun assertThatConnectivityIsPropagatedDuringChange(lastType: Int, currentType: Int) = runTest { + // given val last = Connectivity( type = lastType, state = NetworkInfo.State.CONNECTED @@ -268,18 +257,17 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { detailedState = NetworkInfo.DetailedState.CONNECTED ) // when - runBlockingTest { - val testFlow = - strategy.propagateAnyConnectedState(last, current).testIn(scope = this) + strategy.propagateAnyConnectedState(last, current).test { // then - testFlow expect emissionCount(2) - testFlow expect emissions(current, last) + assertThat(awaitItem()).isEqualTo(current) + assertThat(awaitItem()).isEqualTo(last) + cancelAndConsumeRemainingEvents() } } - @OptIn(ExperimentalCoroutinesApi::class) @Test - fun shouldNotPropagateLastConnectivityEventWhenTypeIsNotChanged() { // given + fun shouldNotPropagateLastConnectivityEventWhenTypeIsNotChanged() = runTest { + // given val last = Connectivity( type = ConnectivityManager.TYPE_WIFI, state = NetworkInfo.State.CONNECTED @@ -290,19 +278,17 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { detailedState = NetworkInfo.DetailedState.CONNECTED ) // when - runBlockingTest { - val testFlow = - strategy.propagateAnyConnectedState(last, current).testIn(scope = this) - // then - testFlow expect emissionCount(1) - testFlow expect emissions(current) + strategy.propagateAnyConnectedState(last, current).test { + // then + assertThat(awaitItem()).isEqualTo(current) + awaitComplete() } } - @OptIn(ExperimentalCoroutinesApi::class) @Test - fun shouldNotPropagateLastConnectivityWhenWasNotConnected() { // given + fun shouldNotPropagateLastConnectivityWhenWasNotConnected() = runTest { + // given val last = Connectivity( type = ConnectivityManager.TYPE_WIFI, state = NetworkInfo.State.DISCONNECTED @@ -313,19 +299,15 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { detailedState = NetworkInfo.DetailedState.CONNECTED ) // when - runBlockingTest { - val testFlow = - strategy.propagateAnyConnectedState(last, current).testIn(scope = this) + strategy.propagateAnyConnectedState(last, current).test { // then - - testFlow expect emissionCount(1) - testFlow expect emissions(current) + assertThat(awaitItem()).isEqualTo(current) + awaitComplete() } } - @OptIn(ExperimentalCoroutinesApi::class) @Test - fun shouldNotPropagateLastConnectivityWhenIsConnected() { // given + fun shouldNotPropagateLastConnectivityWhenIsConnected() = runTest { val last = Connectivity( type = ConnectivityManager.TYPE_WIFI, state = NetworkInfo.State.CONNECTED @@ -336,19 +318,16 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { detailedState = NetworkInfo.DetailedState.CONNECTED ) // when - runBlockingTest { - val testFlow = - strategy.propagateAnyConnectedState(last, current).testIn(scope = this) + strategy.propagateAnyConnectedState(last, current).test { // then - - testFlow expect emissionCount(1) - testFlow expect emissions(current) + assertThat(awaitItem()).isEqualTo(current) + awaitComplete() } } - @OptIn(ExperimentalCoroutinesApi::class) @Test - fun shouldNotPropagateLastConnectivityWhenIsIdle() { // given + fun shouldNotPropagateLastConnectivityWhenIsIdle() = runTest { + // given val last = Connectivity( type = ConnectivityManager.TYPE_WIFI, state = NetworkInfo.State.CONNECTED @@ -359,12 +338,11 @@ open class MarshmallowNetworkObservingStrategyTest : BaseFlowTest() { detailedState = NetworkInfo.DetailedState.IDLE ) // when - runBlockingTest { - val testFlow = strategy.propagateAnyConnectedState(last, current).testIn(scope = this) - // then - testFlow expect emissionCount(1) - testFlow expect emissions(current) + strategy.propagateAnyConnectedState(last, current).test { + // then + assertThat(awaitItem()).isEqualTo(current) + awaitComplete() } } -} \ No newline at end of file +} diff --git a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/PreLollipopNetworkObservingStrategyTest.kt b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/PreLollipopNetworkObservingStrategyTest.kt index a8fba72..b664936 100644 --- a/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/PreLollipopNetworkObservingStrategyTest.kt +++ b/reactiveNetwork/src/test/kotlin/ru/beryukhov/reactivenetwork/network/observing/strategy/PreLollipopNetworkObservingStrategyTest.kt @@ -4,58 +4,38 @@ import android.content.BroadcastReceiver import android.content.Context import android.net.NetworkInfo import androidx.test.core.app.ApplicationProvider +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat import io.mockk.mockk import io.mockk.spyk import io.mockk.verify -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.map -import kotlinx.coroutines.test.runBlockingTest -import org.junit.Ignore +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import ru.beryukhov.reactivenetwork.base.emission -import ru.beryukhov.reactivenetwork.base.expect -import ru.beryukhov.reactivenetwork.base.testIn import ru.beryukhov.reactivenetwork.network.observing.NetworkObservingStrategy @RunWith(RobolectricTestRunner::class) open class PreLollipopNetworkObservingStrategyTest { - @OptIn(ExperimentalCoroutinesApi::class) - @Ignore @Test - fun shouldObserveConnectivity() { // given + fun shouldObserveConnectivity() = runTest { + // given val strategy: NetworkObservingStrategy = PreLollipopNetworkObservingStrategy() val context = ApplicationProvider.getApplicationContext() // when - runBlockingTest { - val testFlow = - strategy.observeNetworkConnectivity(context).map { it.state }.testIn(scope = this) - advanceTimeBy(1000) + strategy.observeNetworkConnectivity(context).map { it.state }.test { + delay(1000) // then - testFlow expect emission(index = 0, expected = NetworkInfo.State.CONNECTED) - + assertThat(awaitItem()).isEqualTo(NetworkInfo.State.CONNECTED) } } - //Rx specific test - /*@Test - fun shouldStopObservingConnectivity() { // given - val strategy: NetworkObservingStrategy = PreLollipopNetworkObservingStrategy() - val context = ApplicationProvider.getApplicationContext() - val observable: Observable = strategy.observeNetworkConnectivity(context) - val observer: TestObserver = TestObserver() - // when - observable.subscribe(observer) - observer.dispose() - // then - assertThat(observer.isDisposed()).isTrue() - }*/ - @Test - fun shouldCallOnError() { // given + fun shouldCallOnError() { + // given val message = "error message" val exception = Exception() val strategy = spyk(PreLollipopNetworkObservingStrategy()) @@ -66,7 +46,8 @@ open class PreLollipopNetworkObservingStrategyTest { } @Test - fun shouldTryToUnregisterReceiver() { // given + fun shouldTryToUnregisterReceiver() { + // given val strategy = PreLollipopNetworkObservingStrategy() val context = spyk(ApplicationProvider.getApplicationContext()) val broadcastReceiver = mockk(relaxed = true) @@ -76,21 +57,17 @@ open class PreLollipopNetworkObservingStrategyTest { verify { context.unregisterReceiver(broadcastReceiver) } } - @OptIn(ExperimentalCoroutinesApi::class) - @Ignore @Test - fun shouldTryToUnregisterReceiverAfterDispose() { // given + fun shouldTryToUnregisterReceiverAfterDispose() = runTest { + // given val context = ApplicationProvider.getApplicationContext() val strategy = spyk(PreLollipopNetworkObservingStrategy()) // when - runBlockingTest { - - val testFlow = strategy.observeNetworkConnectivity(context).testIn(scope = this) - this.cancel() - - // then - verify { strategy.tryToUnregisterReceiver(context, any()) } + strategy.observeNetworkConnectivity(context).test { + cancelAndConsumeRemainingEvents() } + // then + verify { strategy.tryToUnregisterReceiver(context, any()) } } -} \ No newline at end of file +} diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 978c194..961a028 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -4,12 +4,13 @@ plugins { } android { - compileSdkVersion(30) + namespace = "ru.beryukhov.sample" + compileSdk = libs.versions.compileSdk.get().toInt() defaultConfig { applicationId = "ru.beryukhov.sample" - minSdkVersion(21) - targetSdkVersion(30) + minSdk = libs.versions.minSdk.get().toInt() + targetSdk = libs.versions.targetSdk.get().toInt() versionCode = 1 versionName = "1.0" @@ -17,34 +18,25 @@ android { } buildTypes { - getByName("release") { + release { isMinifyEnabled = false - proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt")) - proguardFiles("proguard-rules.pro") + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = "1.8" + targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_17 } } dependencies { +// implementation(flowreactivenetwork) + implementation(projects.reactiveNetwork) - implementation("org.jetbrains.kotlin:kotlin-stdlib:${rootProject.extra["kotlin_version"]}") - implementation("androidx.core:core-ktx:1.6.0") - implementation("androidx.appcompat:appcompat:1.3.1") - implementation("com.google.android.material:material:1.4.0") - implementation("androidx.constraintlayout:constraintlayout:2.1.0") - - implementation("ru.beryukhov:flowreactivenetwork:1.0.4") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${rootProject.extra["coroutines_version"]}") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:${rootProject.extra["coroutines_version"]}") - - testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.3") - androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0") -} \ No newline at end of file + implementation(libs.coroutines.core) + implementation(libs.coreKtx) + implementation(libs.material) +} diff --git a/sample/src/androidTest/java/ru/beryukhov/sample/ExampleInstrumentedTest.kt b/sample/src/androidTest/java/ru/beryukhov/sample/ExampleInstrumentedTest.kt deleted file mode 100644 index e1a9f24..0000000 --- a/sample/src/androidTest/java/ru/beryukhov/sample/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ru.beryukhov.sample - -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.ext.junit.runners.AndroidJUnit4 - -import org.junit.Test -import org.junit.runner.RunWith - -import org.junit.Assert.* - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("ru.beryukhov.sample", appContext.packageName) - } -} \ No newline at end of file diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index c95023e..0a04aa9 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,16 +1,14 @@ - + - + android:supportsRtl="true"> + diff --git a/sample/src/main/java/ru/beryukhov/sample/MainActivity.kt b/sample/src/main/java/ru/beryukhov/sample/MainActivity.kt index cd89da6..42dd6ff 100644 --- a/sample/src/main/java/ru/beryukhov/sample/MainActivity.kt +++ b/sample/src/main/java/ru/beryukhov/sample/MainActivity.kt @@ -1,15 +1,15 @@ package ru.beryukhov.sample -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log +import androidx.activity.ComponentActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import ru.beryukhov.reactivenetwork.* +import ru.beryukhov.reactivenetwork.ReactiveNetwork -class MainActivity : AppCompatActivity() { +class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -26,4 +26,4 @@ class MainActivity : AppCompatActivity() { Log.i("MainActivity", "NetworkConnectivity changed on $it") }.launchIn(CoroutineScope(Dispatchers.Default)) } -} \ No newline at end of file +} diff --git a/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/sample/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/sample/src/main/res/drawable/ic_launcher_background.xml b/sample/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9..0000000 --- a/sample/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 4fc2444..27cc83e 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,6 +1,5 @@ - + android:text="Hello World!" /> - \ No newline at end of file + \ No newline at end of file diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cf..0000000 --- a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cf..0000000 --- a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher.png b/sample/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a571e60..0000000 Binary files a/sample/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 61da551..0000000 Binary files a/sample/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher.png b/sample/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index c41dd28..0000000 Binary files a/sample/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index db5080a..0000000 Binary files a/sample/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 6dba46d..0000000 Binary files a/sample/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index da31a87..0000000 Binary files a/sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 15ac681..0000000 Binary files a/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index b216f2d..0000000 Binary files a/sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index e96783c..0000000 Binary files a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/sample/src/main/res/mipmap/ic_launcher.png similarity index 100% rename from sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to sample/src/main/res/mipmap/ic_launcher.png diff --git a/sample/src/main/res/values-night/themes.xml b/sample/src/main/res/values-night/themes.xml deleted file mode 100644 index 32c913f..0000000 --- a/sample/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - \ No newline at end of file diff --git a/sample/src/main/res/values/themes.xml b/sample/src/main/res/values/themes.xml deleted file mode 100644 index 02fe50b..0000000 --- a/sample/src/main/res/values/themes.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index c7e0a70..0000000 --- a/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -include ':sample' -include ':reactiveNetwork' -rootProject.name = "FlowReactiveNetwork" \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..8007e8e --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,7 @@ +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +rootProject.name = "FlowReactiveNetwork" +include(":sample") +include(":reactiveNetwork") + +includeBuild("build-logic")