mirror of
https://github.com/InsanusMokrassar/krontab.git
synced 2024-11-26 03:58:50 +00:00
start 0.10.0 + rework of flows
This commit is contained in:
parent
7da67386cf
commit
5a1ed2f933
@ -1,5 +1,7 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.10.0
|
||||||
|
|
||||||
## 0.9.0
|
## 0.9.0
|
||||||
|
|
||||||
* Versions
|
* Versions
|
||||||
|
@ -10,7 +10,7 @@ buildscript {
|
|||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
|
classpath "com.github.breadmoirai:github-release:$github_release_plugin_version"
|
||||||
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
|
classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version"
|
||||||
classpath 'com.android.tools.build:gradle:7.0.4'
|
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ kotlin {
|
|||||||
android {
|
android {
|
||||||
publishAllLibraryVariants()
|
publishAllLibraryVariants()
|
||||||
}
|
}
|
||||||
|
linuxX64()
|
||||||
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -79,6 +80,7 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('test-common')
|
implementation kotlin('test-common')
|
||||||
implementation kotlin('test-annotations-common')
|
implementation kotlin('test-annotations-common')
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jvmTest {
|
jvmTest {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx512m
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
kotlin.js.generate.externals=true
|
kotlin.js.generate.externals=true
|
||||||
@ -33,5 +34,5 @@ androidx_work_version=2.8.0
|
|||||||
|
|
||||||
## Common
|
## Common
|
||||||
|
|
||||||
version=0.9.0
|
version=0.10.0
|
||||||
android_code_version=23
|
android_code_version=24
|
||||||
|
@ -2,35 +2,96 @@ package dev.inmo.krontab.utils
|
|||||||
|
|
||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import com.soywiz.klock.DateTimeTz
|
import com.soywiz.klock.DateTimeTz
|
||||||
|
import com.soywiz.klock.milliseconds
|
||||||
import dev.inmo.krontab.*
|
import dev.inmo.krontab.*
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.channelFlow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This [Flow] will trigger emitting each near time which will be returned from [this] [KronScheduler] with attention to
|
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
|
||||||
* time zones
|
|
||||||
*
|
*
|
||||||
* @see channelFlow
|
* Will emit all the [KronScheduler.next] as soon as possible. In case [KronScheduler.next] return null, flow will
|
||||||
* @see KronSchedulerTz.doInfinityTz
|
* be completed
|
||||||
|
*
|
||||||
|
* @param since Will be used as the first parameter for [KronScheduler.next] fun
|
||||||
*/
|
*/
|
||||||
@FlowPreview
|
@FlowPreview
|
||||||
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = channelFlow {
|
fun KronScheduler.asTzFlowWithoutDelays(since: DateTimeTz = DateTime.nowLocal()): Flow<DateTimeTz> = flow {
|
||||||
doInfinityTz {
|
var previous = since
|
||||||
send(it)
|
while (currentCoroutineContext().isActive) {
|
||||||
|
val next = next(previous) ?: break
|
||||||
|
emit(next)
|
||||||
|
previous = next + 1.milliseconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is a map for [asTzFlow] and will works the same but return flow with [DateTime]s
|
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
|
||||||
*
|
*
|
||||||
* @see channelFlow
|
* This [Flow] will use [asTzFlowWithoutDelays], but stop on each time until this time will happen
|
||||||
* @see KronScheduler.doInfinity
|
|
||||||
*/
|
*/
|
||||||
@FlowPreview
|
@FlowPreview
|
||||||
fun KronScheduler.asFlow(): Flow<DateTime> = channelFlow {
|
fun KronScheduler.asTzFlowWithDelays(): Flow<DateTimeTz> = asTzFlowWithoutDelays().onEach { futureHappenTime ->
|
||||||
doInfinity {
|
val now = DateTime.nowLocal()
|
||||||
send(it)
|
|
||||||
|
delay((futureHappenTime - now).millisecondsLong)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
|
||||||
|
*
|
||||||
|
* This [Flow] will use [asTzFlowWithoutDelays], but stop on each time until this time will happen
|
||||||
|
*/
|
||||||
|
@Deprecated(
|
||||||
|
"Behaviour will be changed. In some of near versions this flow will not delay executions",
|
||||||
|
ReplaceWith("this.asTzFlowWithDelays()", "dev.inmo.krontab.utils.asTzFlowWithDelays")
|
||||||
|
)
|
||||||
|
@FlowPreview
|
||||||
|
fun KronScheduler.asTzFlow(): Flow<DateTimeTz> = asTzFlowWithDelays()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
|
||||||
|
*
|
||||||
|
* Will emit all the [KronScheduler.next] as soon as possible. In case [KronScheduler.next] return null, flow will
|
||||||
|
* be completed
|
||||||
|
*
|
||||||
|
* @param since Will be used as the first parameter for [KronScheduler.next] fun
|
||||||
|
*/
|
||||||
|
@FlowPreview
|
||||||
|
fun KronScheduler.asFlowWithoutDelays(since: DateTime = DateTime.now()): Flow<DateTime> = flow {
|
||||||
|
var previous = since
|
||||||
|
while (currentCoroutineContext().isActive) {
|
||||||
|
val next = next(previous) ?: break
|
||||||
|
emit(next)
|
||||||
|
previous = next + 1.milliseconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
|
||||||
|
*
|
||||||
|
* This [Flow] will use [asFlowWithoutDelays], but stop on each time until this time will happen
|
||||||
|
*/
|
||||||
|
@FlowPreview
|
||||||
|
fun KronScheduler.asFlowWithDelays(): Flow<DateTime> = asFlowWithoutDelays().onEach { futureHappenTime ->
|
||||||
|
val now = DateTime.now()
|
||||||
|
|
||||||
|
delay((futureHappenTime - now).millisecondsLong)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **This flow is [cold](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/)**
|
||||||
|
*
|
||||||
|
* This [Flow] will use [asFlowWithDelays], but stop on each time until this time will happen
|
||||||
|
*/
|
||||||
|
@Deprecated(
|
||||||
|
"Behaviour will be changed. In some of near versions this flow will not delay executions",
|
||||||
|
ReplaceWith("this.asFlowWithDelays()", "dev.inmo.krontab.utils.asFlowWithDelays")
|
||||||
|
)
|
||||||
|
@FlowPreview
|
||||||
|
fun KronScheduler.asFlow(): Flow<DateTime> = asFlowWithDelays()
|
||||||
|
@ -3,6 +3,7 @@ package dev.inmo.krontab.utils
|
|||||||
import com.soywiz.klock.DateTime
|
import com.soywiz.klock.DateTime
|
||||||
import com.soywiz.klock.days
|
import com.soywiz.klock.days
|
||||||
import dev.inmo.krontab.buildSchedule
|
import dev.inmo.krontab.buildSchedule
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
class CheckMonthsAndDaysCorrectWork {
|
class CheckMonthsAndDaysCorrectWork {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package dev.inmo.krontab.utils
|
package dev.inmo.krontab.utils
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
fun CoroutineScope.createFailJob(forTimeMillis: Long) = launch {
|
fun CoroutineScope.createFailJob(forTimeMillis: Long) = launch {
|
||||||
delay(forTimeMillis)
|
delay(forTimeMillis)
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
package dev.inmo.krontab.utils
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround to use suspending functions in unit tests
|
|
||||||
*/
|
|
||||||
expect fun runTest(block: suspend (scope : CoroutineScope) -> Unit)
|
|
@ -2,8 +2,8 @@ package dev.inmo.krontab.utils
|
|||||||
|
|
||||||
import dev.inmo.krontab.builder.buildSchedule
|
import dev.inmo.krontab.builder.buildSchedule
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.takeWhile
|
import kotlinx.coroutines.flow.takeWhile
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ class SchedulerFlowTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val flow = kronScheduler.asFlow()
|
val flow = kronScheduler.asFlowWithoutDelays()
|
||||||
|
|
||||||
runTest {
|
runTest {
|
||||||
val mustBeCollected = 10
|
val mustBeCollected = 10
|
||||||
@ -40,14 +40,14 @@ class SchedulerFlowTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val flow = kronScheduler.asFlow()
|
val flow = kronScheduler.asFlowWithoutDelays()
|
||||||
|
|
||||||
runTest {
|
runTest {
|
||||||
val testsCount = 10
|
val testsCount = 10
|
||||||
val failJob = it.createFailJob((testsCount * 2) * 1000L)
|
val failJob = createFailJob((testsCount * 2) * 1000L)
|
||||||
val mustBeCollected = 10
|
val mustBeCollected = 10
|
||||||
val answers = (0 until testsCount).map { _ ->
|
val answers = (0 until testsCount).map { _ ->
|
||||||
it.async {
|
async {
|
||||||
var collected = 0
|
var collected = 0
|
||||||
flow.takeWhile {
|
flow.takeWhile {
|
||||||
collected < mustBeCollected
|
collected < mustBeCollected
|
||||||
|
@ -4,9 +4,8 @@ import com.soywiz.klock.*
|
|||||||
import dev.inmo.krontab.KronSchedulerTz
|
import dev.inmo.krontab.KronSchedulerTz
|
||||||
import dev.inmo.krontab.buildSchedule
|
import dev.inmo.krontab.buildSchedule
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.takeWhile
|
import kotlinx.coroutines.flow.takeWhile
|
||||||
import kotlin.math.floor
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
@ -16,7 +15,7 @@ class StringParseTest {
|
|||||||
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnString() {
|
fun testThatFlowIsCorrectlyWorkEverySecondBuiltOnString() {
|
||||||
val kronScheduler = buildSchedule("*/1 * * * *")
|
val kronScheduler = buildSchedule("*/1 * * * *")
|
||||||
|
|
||||||
val flow = kronScheduler.asFlow()
|
val flow = kronScheduler.asFlowWithoutDelays()
|
||||||
|
|
||||||
runTest {
|
runTest {
|
||||||
val mustBeCollected = 10
|
val mustBeCollected = 10
|
||||||
@ -33,7 +32,7 @@ class StringParseTest {
|
|||||||
fun testThatFlowIsCorrectlyWorkEverySecondWhenMillisIsHalfOfSecondBuiltOnString() {
|
fun testThatFlowIsCorrectlyWorkEverySecondWhenMillisIsHalfOfSecondBuiltOnString() {
|
||||||
val kronScheduler = buildSchedule("*/1 * * * * 500ms")
|
val kronScheduler = buildSchedule("*/1 * * * * 500ms")
|
||||||
|
|
||||||
val flow = kronScheduler.asFlow()
|
val flow = kronScheduler.asFlowWithoutDelays()
|
||||||
|
|
||||||
runTest {
|
runTest {
|
||||||
val mustBeCollected = 10
|
val mustBeCollected = 10
|
||||||
@ -51,14 +50,14 @@ class StringParseTest {
|
|||||||
fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmittersBuiltOnString() {
|
fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmittersBuiltOnString() {
|
||||||
val kronScheduler = buildSchedule("*/1 * * * *")
|
val kronScheduler = buildSchedule("*/1 * * * *")
|
||||||
|
|
||||||
val flow = kronScheduler.asFlow()
|
val flow = kronScheduler.asFlowWithoutDelays()
|
||||||
|
|
||||||
runTest {
|
runTest {
|
||||||
val testsCount = 10
|
val testsCount = 10
|
||||||
val failJob = it.createFailJob((testsCount * 2) * 1000L)
|
val failJob = createFailJob((testsCount * 2) * 1000L)
|
||||||
val mustBeCollected = 10
|
val mustBeCollected = 10
|
||||||
val answers = (0 until testsCount).map { _ ->
|
val answers = (0 until testsCount).map { _ ->
|
||||||
it.async {
|
async {
|
||||||
var collected = 0
|
var collected = 0
|
||||||
flow.takeWhile {
|
flow.takeWhile {
|
||||||
collected < mustBeCollected
|
collected < mustBeCollected
|
||||||
@ -81,7 +80,7 @@ class StringParseTest {
|
|||||||
val rangesEnds = listOf(0 to 5, 30 to 35)
|
val rangesEnds = listOf(0 to 5, 30 to 35)
|
||||||
val kronScheduler = buildSchedule("${rangesEnds.joinToString(",") { "${it.first}-${it.second}" }} * * * *")
|
val kronScheduler = buildSchedule("${rangesEnds.joinToString(",") { "${it.first}-${it.second}" }} * * * *")
|
||||||
|
|
||||||
val flow = kronScheduler.asFlow()
|
val flow = kronScheduler.asFlowWithoutDelays()
|
||||||
|
|
||||||
runTest {
|
runTest {
|
||||||
val ranges = rangesEnds.map { it.first .. it.second }.flatten().distinct().toMutableList()
|
val ranges = rangesEnds.map { it.first .. it.second }.flatten().distinct().toMutableList()
|
||||||
@ -91,7 +90,10 @@ class StringParseTest {
|
|||||||
flow.takeWhile { ranges.isNotEmpty() }.collect {
|
flow.takeWhile { ranges.isNotEmpty() }.collect {
|
||||||
ranges.remove(it.seconds)
|
ranges.remove(it.seconds)
|
||||||
collected++
|
collected++
|
||||||
assertTrue(collected <= expectedCollects)
|
assertTrue(
|
||||||
|
collected <= expectedCollects,
|
||||||
|
"Expected value should be less than $expectedCollects, but was $collected. Ranges state: $ranges"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
assertEquals(expectedCollects, collected)
|
assertEquals(expectedCollects, collected)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package dev.inmo.krontab.utils
|
|||||||
import com.soywiz.klock.*
|
import com.soywiz.klock.*
|
||||||
import dev.inmo.krontab.builder.buildSchedule
|
import dev.inmo.krontab.builder.buildSchedule
|
||||||
import dev.inmo.krontab.next
|
import dev.inmo.krontab.next
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package dev.inmo.krontab.utils
|
|||||||
|
|
||||||
import com.soywiz.klock.*
|
import com.soywiz.klock.*
|
||||||
import dev.inmo.krontab.builder.buildSchedule
|
import dev.inmo.krontab.builder.buildSchedule
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
package dev.inmo.krontab.utils
|
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
|
|
||||||
actual fun runTest(block: suspend (scope : CoroutineScope) -> Unit): dynamic = GlobalScope.promise { block(this) }
|
|
@ -1,9 +0,0 @@
|
|||||||
package dev.inmo.krontab.utils
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Workaround to use suspending functions in unit tests
|
|
||||||
*/
|
|
||||||
actual fun runTest(block: suspend (scope: CoroutineScope) -> Unit) = runBlocking(block = block)
|
|
Loading…
Reference in New Issue
Block a user