Merge branch '0.2.1' of InsanusMokrassar/krontab into master

This commit is contained in:
Aleksei Ovsiannikov 2020-01-13 16:02:27 +00:00 committed by Gogs
commit 394522db6b
10 changed files with 177 additions and 1 deletions

View File

@ -6,3 +6,7 @@
* Updates in libraries: * Updates in libraries:
* Coroutines `1.3.2` -> `1.3.3` * Coroutines `1.3.2` -> `1.3.3`
* Klock `1.7.3` -> `1.8.6` * Klock `1.7.3` -> `1.8.6`
## 0.2.1
* Added support of flows: now any `KronScheduler` can be convert to `Flow<DateTime>` using `asFlow` extension

View File

@ -11,6 +11,7 @@ runtime of applications.
| [ How to use: Including in project ](#including-in-project) | | [ How to use: Including in project ](#including-in-project) |
| [ How to use: Config from string ](#config-from-string) | | [ How to use: Config from string ](#config-from-string) |
| [ How to use: Config via builder (DSL preview) ](#config-via-builder) | | [ How to use: Config via builder (DSL preview) ](#config-via-builder) |
| [ How to use: KronScheduler as a Flow ](#KronScheduler-as-a-Flow) |
## How to use ## How to use
@ -124,3 +125,28 @@ kronScheduler.doInfinity {
``` ```
All of these examples will do the same things: print `Called` message every five seconds. All of these examples will do the same things: print `Called` message every five seconds.
### KronScheduler as a Flow
Any `KronScheduler`can e converted to a `Flow<DateTime` using extension `asFlow`:
```kotlin
val kronScheduler = buildSchedule {
seconds {
0 every 1
}
}
val flow = kronScheduler.asFlow()
```
So, in this case any operations related to flow are available and it is expected tt th will work correctly. For example,
it is possible to use this flow with `takeWhile`:
```kotlin
flow.takeWhile {
condition()
}.collect {
action()
}
```

View File

@ -17,7 +17,7 @@ plugins {
id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version" id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version"
} }
project.version = "0.2.0" project.version = "0.2.1"
project.group = "com.insanusmokrassar" project.group = "com.insanusmokrassar"
apply from: "publish.gradle" apply from: "publish.gradle"
@ -47,6 +47,7 @@ kotlin {
implementation kotlin('test-common') implementation kotlin('test-common')
implementation kotlin('test-annotations-common') implementation kotlin('test-annotations-common')
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version"
} }
} }
jvmMain { jvmMain {

View File

@ -2,9 +2,30 @@ apply plugin: 'com.jfrog.bintray'
apply from: "maven.publish.gradle" apply from: "maven.publish.gradle"
ext {
projectBintrayDir = "${project.group}/".replace(".", "/") + "${project.name}/${project.version}"
}
bintray { bintray {
user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER') user = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER')
key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY') key = project.hasProperty('BINTRAY_KEY') ? project.property('BINTRAY_KEY') : System.getenv('BINTRAY_KEY')
filesSpec {
from "${buildDir}/publications/"
eachFile {
if (it.getName() == "module.json") {
File file = it.getFile()
String directorySubname = file.parentFile.name
if (directorySubname == "kotlinMultiplatform") {
it.setPath("${project.name}/${project.version}/${project.name}-${project.version}.module")
} else {
it.setPath("${project.name}-${directorySubname}/${project.version}/${project.name}-${directorySubname}-${project.version}.module")
}
} else {
it.exclude()
}
}
into "${project.group}".replace(".", "/")
}
pkg { pkg {
repo = "InsanusMokrassar" repo = "InsanusMokrassar"
name = "${project.name}" name = "${project.name}"

View File

@ -0,0 +1,25 @@
package com.insanusmokrassar.krontab.utils
import com.insanusmokrassar.krontab.KronScheduler
import com.soywiz.klock.DateTime
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
@FlowPreview
fun KronScheduler.asFlow(): Flow<DateTime> = SchedulerFlow(this)
@FlowPreview
class SchedulerFlow(
private val scheduler: KronScheduler
) : AbstractFlow<DateTime>() {
@FlowPreview
override suspend fun collectSafely(collector: FlowCollector<DateTime>) {
while (true) {
val now = DateTime.now()
val nextTime = scheduler.next(now)
val sleepDelay = (nextTime - now).millisecondsLong
delay(sleepDelay)
collector.emit(nextTime)
}
}
}

View File

@ -0,0 +1,8 @@
package com.insanusmokrassar.krontab.utils
import kotlinx.coroutines.*
fun CoroutineScope.createFailJob(forTimeMillis: Long) = launch {
delay(forTimeMillis)
throw IllegalStateException("This job must be completed at before this exception happen")
}

View File

@ -0,0 +1,8 @@
package com.insanusmokrassar.krontab.utils
import kotlinx.coroutines.CoroutineScope
/**
* Workaround to use suspending functions in unit tests
*/
expect fun runTest(block: suspend (scope : CoroutineScope) -> Unit)

View File

@ -0,0 +1,69 @@
package com.insanusmokrassar.krontab.utils
import com.insanusmokrassar.krontab.builder.buildSchedule
import com.soywiz.klock.DateTime
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.takeWhile
import kotlin.test.Test
import kotlin.test.assertEquals
@ExperimentalCoroutinesApi
@FlowPreview
class SchedulerFlowTests {
@Test
fun testThatFlowIsCorrectlyWorkEverySecond() {
val kronScheduler = buildSchedule {
seconds {
0 every 1
}
}
val flow = kronScheduler.asFlow()
runTest {
val mustBeCollected = 10
var collected = 0
flow.takeWhile {
collected < mustBeCollected
}.collect {
collected++
}
assertEquals(mustBeCollected, collected)
}
}
@Test
fun testThatFlowIsCorrectlyWorkEverySecondWithMuchOfEmitters() {
val kronScheduler = buildSchedule {
seconds {
0 every 1
}
}
val flow = kronScheduler.asFlow()
runTest {
val testsCount = 10
val failJob = it.createFailJob((testsCount * 2) * 1000L)
val mustBeCollected = 10
val answers = (0 until testsCount).map { _ ->
it.async {
var collected = 0
flow.takeWhile {
collected < mustBeCollected
}.collect {
collected++
}
collected
}
}.awaitAll()
failJob.cancel()
answers.forEach {
assertEquals(mustBeCollected, it)
}
}
}
}

View File

@ -0,0 +1,5 @@
package com.insanusmokrassar.krontab.utils
import kotlinx.coroutines.*
actual fun runTest(block: suspend (scope : CoroutineScope) -> Unit): dynamic = GlobalScope.promise { block(this) }

View File

@ -0,0 +1,9 @@
package com.insanusmokrassar.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)